diff options
800 files changed, 2097 insertions, 66832 deletions
diff --git a/anttasks/Android.mk b/anttasks/Android.mk index 9922f53..ae0c3a9 100644 --- a/anttasks/Android.mk +++ b/anttasks/Android.mk @@ -25,7 +25,8 @@ LOCAL_JAVA_LIBRARIES := \ common \ sdklib \ manifmerger \ - ant + ant \ + guava-tools LOCAL_MODULE := anttasks diff --git a/anttasks/src/com/android/ant/AaptExecTask.java b/anttasks/src/com/android/ant/AaptExecTask.java index 257d1b9..a1d44c4 100644 --- a/anttasks/src/com/android/ant/AaptExecTask.java +++ b/anttasks/src/com/android/ant/AaptExecTask.java @@ -20,8 +20,8 @@ import com.android.SdkConstants; import com.android.sdklib.internal.build.SymbolLoader; import com.android.sdklib.internal.build.SymbolWriter; import com.android.xml.AndroidXPathFactory; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; @@ -32,12 +32,10 @@ import org.xml.sax.InputSource; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import javax.xml.xpath.XPath; @@ -401,6 +399,7 @@ public final class AaptExecTask extends SingleDependencyTask { * * @see org.apache.tools.ant.Task#execute() */ + @SuppressWarnings("deprecation") @Override public void execute() throws BuildException { if (mLibraryResFolderPathRefid == null) { @@ -706,8 +705,8 @@ public final class AaptExecTask extends SingleDependencyTask { File rFile = new File(mBinFolder, SdkConstants.FN_RESOURCE_TEXT); if (rFile.isFile()) { // Load the full symbols from the full R.txt file. - SymbolLoader fullSymbols = new SymbolLoader(rFile); - fullSymbols.load(); + SymbolLoader fullSymbolValues = new SymbolLoader(rFile); + fullSymbolValues.load(); // we have two props which contains list of items. Both items represent // 2 data of a single property. @@ -729,66 +728,41 @@ public final class AaptExecTask extends SingleDependencyTask { mOriginalManifestPackage = getPackageName(mManifestFile); } - // simpler case of a single library - if (packages.length == 1) { - if (!mOriginalManifestPackage.equals(packages[0])) { - createRClass(fullSymbols, rFiles[0], packages[0]); + Multimap<String, SymbolLoader> libMap = ArrayListMultimap.create(); + + // First pass processing the libraries, collecting them by packageName, + // and ignoring the ones that have the same package name as the application + // (since that R class was already created). + for (int i = 0 ; i < packages.length ; i++) { + String libPackage = packages[i]; + + // skip libraries that have the same package name as the application. + if (mOriginalManifestPackage.equals(libPackage)) { + continue; } - } else { - - Map<String, String> libPackages = Maps.newHashMapWithExpectedSize( - packages.length); - Set<String> duplicatePackages = Sets.newHashSet(); - - // preprocessing to figure out if there are dups in the package names of - // the libraries - for (int i = 0 ; i < packages.length ; i++) { - String libPackage = packages[i]; - if (mOriginalManifestPackage.equals(libPackage)) { - // skip libraries that have the same package name as the application. - continue; - } - - String existingPkg = libPackages.get(libPackage); - if (existingPkg != null) { - // record the dup package and keep going, in case there are all the same - duplicatePackages.add(libPackage); - continue; - } - - libPackages.put(libPackage, rFiles[i]); + + File rText = new File(rFiles[i]); + if (rText.isFile()) { + // load the lib symbols + SymbolLoader libSymbols = new SymbolLoader(rText); + libSymbols.load(); + + // store these symbols by associating them with the package name. + libMap.put(libPackage, libSymbols); } + } - // check if we have duplicate but all files are the same. - if (duplicatePackages.size() > 0) { - // possible conflict! - // detect case of all libraries == same package. - if (duplicatePackages.size() == 1 && libPackages.size() == 1 && - duplicatePackages.iterator().next().equals(libPackages.keySet().iterator().next())) { - // this is ok, all libraries have the same package. - // Make a copy of the full R class. - SymbolWriter writer = new SymbolWriter(mRFolder, - duplicatePackages.iterator().next(), - fullSymbols, fullSymbols); - writer.write(); - } else { - StringBuilder sb = new StringBuilder(); - sb.append("The following packages have been found to be used by two or more libraries:"); - for (String pkg : duplicatePackages) { - sb.append("\n\t").append(pkg); - } - sb.append("\nNo libraries must share the same package, unless all libraries share the same packages."); - throw new BuildException(sb.toString()); - } - } else { - // no dups, all libraries have different packages. - // Conflicts with the main package have been removed already. - // Just process all the libraries from the list where we removed - // libs that had the same package as the app. - for (Entry<String, String> lib : libPackages.entrySet()) { - createRClass(fullSymbols, lib.getValue(), lib.getKey()); - } + // now loop on all the package names, merge all the symbols to write, + // and write them + for (String packageName : libMap.keySet()) { + Collection<SymbolLoader> symbols = libMap.get(packageName); + + SymbolWriter writer = new SymbolWriter(mRFolder, packageName, + fullSymbolValues); + for (SymbolLoader symbolLoader : symbols) { + writer.addSymbolsToWrite(symbolLoader); } + writer.write(); } } } @@ -803,18 +777,6 @@ public final class AaptExecTask extends SingleDependencyTask { } } - private void createRClass(SymbolLoader fullSymbols, String libRTxtFile, String libPackage) - throws IOException { - File libSymbolFile = new File(libRTxtFile); - if (libSymbolFile.isFile()) { - SymbolLoader libSymbols = new SymbolLoader(libSymbolFile); - libSymbols.load(); - - SymbolWriter writer = new SymbolWriter(mRFolder, libPackage, libSymbols, fullSymbols); - writer.write(); - } - } - private String getPackageName(String manifest) { XPath xpath = AndroidXPathFactory.newXPath(); diff --git a/anttasks/src/com/android/ant/TaskHelper.java b/anttasks/src/com/android/ant/TaskHelper.java index 43ea33a..c93b193 100644 --- a/anttasks/src/com/android/ant/TaskHelper.java +++ b/anttasks/src/com/android/ant/TaskHelper.java @@ -22,7 +22,7 @@ import com.android.annotations.Nullable; import com.android.sdklib.internal.project.ProjectProperties; import com.android.sdklib.internal.project.ProjectProperties.PropertyType; import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy; -import com.android.sdklib.internal.repository.packages.FullRevision; +import com.android.sdklib.repository.FullRevision; import com.android.sdklib.repository.PkgProps; import org.apache.tools.ant.BuildException; diff --git a/assetstudio/Android.mk b/assetstudio/Android.mk index 88f4987..17f9415 100644 --- a/assetstudio/Android.mk +++ b/assetstudio/Android.mk @@ -16,8 +16,14 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under,src) -LOCAL_JAVA_RESOURCE_DIRS := src +# The assetstudio code has moved to tools/base/assetstudio. +# The rule below uses the prebuilt assetstudio.jar. +# +# If you want to run the tests, cd to tools/base/assetstudio +# and run ./gradlew :assetstudio:test + +LOCAL_MODULE := assetstudio +LOCAL_MODULE_TAGS := optional # TODO: Replace common with the batik stuff LOCAL_JAVA_LIBRARIES := \ @@ -27,8 +33,8 @@ LOCAL_JAVA_LIBRARIES := \ LOCAL_JAR_MANIFEST := etc/manifest.txt -LOCAL_MODULE := assetstudio +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + ../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) -LOCAL_MODULE_TAGS := optional +include $(BUILD_HOST_PREBUILT) -include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/assetstudio/src/com/android/assetstudiolib/ActionBarIconGenerator.java b/assetstudio/src/com/android/assetstudiolib/ActionBarIconGenerator.java deleted file mode 100644 index 3cd6f11..0000000 --- a/assetstudio/src/com/android/assetstudiolib/ActionBarIconGenerator.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.assetstudiolib; - -import com.android.assetstudiolib.Util.Effect; -import com.android.assetstudiolib.Util.FillEffect; - -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.image.BufferedImage; - -/** - * Generate icons for the action bar - */ -public class ActionBarIconGenerator extends GraphicGenerator { - - /** Creates a new {@link ActionBarIconGenerator} */ - public ActionBarIconGenerator() { - } - - @Override - public BufferedImage generate(GraphicGeneratorContext context, Options options) { - ActionBarOptions actionBarOptions = (ActionBarOptions) options; - Rectangle iconSizeMdpi = new Rectangle(0, 0, 32, 32); - Rectangle targetRectMdpi = actionBarOptions.sourceIsClipart - ? new Rectangle(0, 0, 32, 32) - : new Rectangle(4, 4, 24, 24); - final float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density); - Rectangle imageRect = Util.scaleRectangle(iconSizeMdpi, scaleFactor); - Rectangle targetRect = Util.scaleRectangle(targetRectMdpi, scaleFactor); - BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); - Graphics2D g = (Graphics2D) outImage.getGraphics(); - - BufferedImage tempImage = Util.newArgbBufferedImage( - imageRect.width, imageRect.height); - Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); - Util.drawCenterInside(g2, options.sourceImage, targetRect); - - if (actionBarOptions.theme == Theme.HOLO_LIGHT) { - Util.drawEffects(g, tempImage, 0, 0, new Effect[] { - new FillEffect(new Color(0x333333), 0.6), - }); - } else { - assert actionBarOptions.theme == Theme.HOLO_DARK; - Util.drawEffects(g, tempImage, 0, 0, new Effect[] { - new FillEffect(new Color(0xFFFFFF), 0.8) - }); - } - - g.dispose(); - g2.dispose(); - - return outImage; - } - - /** Options specific to generating action bar icons */ - public static class ActionBarOptions extends GraphicGenerator.Options { - /** The theme to generate icons for */ - public Theme theme = Theme.HOLO_LIGHT; - - /** Whether or not the source image is a clipart source */ - public boolean sourceIsClipart = false; - } - - /** The themes to generate action bar icons for */ - public enum Theme { - /** Theme.Holo - a dark (and default) version of the Honeycomb theme */ - HOLO_DARK, - - /** Theme.HoloLight - a light version of the Honeycomb theme */ - HOLO_LIGHT; - } -} diff --git a/assetstudio/src/com/android/assetstudiolib/GraphicGenerator.java b/assetstudio/src/com/android/assetstudiolib/GraphicGenerator.java deleted file mode 100644 index 706adc8..0000000 --- a/assetstudio/src/com/android/assetstudiolib/GraphicGenerator.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * 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.assetstudiolib; - -import com.android.resources.Density; -import com.google.common.io.Closeables; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URISyntaxException; -import java.net.URL; -import java.security.ProtectionDomain; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.jar.JarFile; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import javax.imageio.ImageIO; - -/** - * The base Generator class. - */ -public abstract class GraphicGenerator { - /** - * Options used for all generators. - */ - public static class Options { - /** Minimum version (API level) of the SDK to generate icons for */ - public int minSdk = 1; - - /** Source image to use as a basis for the icon */ - public BufferedImage sourceImage; - - /** The density to generate the icon with */ - public Density density = Density.XHIGH; - } - - /** Shapes that can be used for icon backgrounds */ - public static enum Shape { - /** No background */ - NONE("none"), - /** Circular background */ - CIRCLE("circle"), - /** Square background */ - SQUARE("square"); - - /** Id, used in filenames to identify associated stencils */ - public final String id; - - Shape(String id) { - this.id = id; - } - } - - /** Foreground effects styles */ - public static enum Style { - /** No effects */ - SIMPLE("fore1"); - - /** Id, used in filenames to identify associated stencils */ - public final String id; - - Style(String id) { - this.id = id; - } - } - - /** - * Generate a single icon using the given options - * - * @param context render context to use for looking up resources etc - * @param options options controlling the appearance of the icon - * @return a {@link BufferedImage} with the generated icon - */ - public abstract BufferedImage generate(GraphicGeneratorContext context, Options options); - - /** - * Computes the target filename (relative to the Android project folder) - * where an icon rendered with the given options should be stored. This is - * also used as the map keys in the result map used by - * {@link #generate(String, Map, GraphicGeneratorContext, Options, String)}. - * - * @param options the options object used by the generator for the current - * image - * @param name the base name to use when creating the path - * @return a path relative to the res/ folder where the image should be - * stored (will always use / as a path separator, not \ on Windows) - */ - protected String getIconPath(Options options, String name) { - return getIconFolder(options) + '/' + getIconName(options, name); - } - - /** - * Gets name of the file itself. It is sometimes modified by options, for - * example in unselected tabs we change foo.png to foo-unselected.png - */ - protected String getIconName(Options options, String name) { - return name + ".png"; //$NON-NLS-1$ - } - - /** - * Gets name of the folder to contain the resource. It usually includes the - * density, but is also sometimes modified by options. For example, in some - * notification icons we add in -v9 or -v11. - */ - protected String getIconFolder(Options options) { - return "res/drawable-" + options.density.getResourceValue(); //$NON-NLS-1$ - } - - /** - * Generates a full set of icons into the given map. The values in the map - * will be the generated images, and each value is keyed by the - * corresponding relative path of the image, which is determined by the - * {@link #getIconPath(Options, String)} method. - * - * @param category the current category to place images into (if null the - * density name will be used) - * @param categoryMap the map to put images into, should not be null. The - * map is a map from a category name, to a map from file path to - * image. - * @param context a generator context which for example can load resources - * @param options options to apply to this generator - * @param name the base name of the icons to generate - */ - public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap, - GraphicGeneratorContext context, Options options, String name) { - Density[] densityValues = Density.values(); - // Sort density values into ascending order - Arrays.sort(densityValues, new Comparator<Density>() { - @Override - public int compare(Density d1, Density d2) { - return d1.getDpiValue() - d2.getDpiValue(); - } - }); - - for (Density density : densityValues) { - if (!density.isValidValueForDevice()) { - continue; - } - if (density == Density.LOW || density == Density.TV || - (density == Density.XXHIGH && !(this instanceof LauncherIconGenerator))) { - // TODO don't manually check and instead gracefully handle missing stencils. - // Not yet supported -- missing stencil image - continue; - } - options.density = density; - BufferedImage image = generate(context, options); - if (image != null) { - String mapCategory = category; - if (mapCategory == null) { - mapCategory = options.density.getResourceValue(); - } - Map<String, BufferedImage> imageMap = categoryMap.get(mapCategory); - if (imageMap == null) { - imageMap = new LinkedHashMap<String, BufferedImage>(); - categoryMap.put(mapCategory, imageMap); - } - imageMap.put(getIconPath(options, name), image); - } - } - } - - /** - * Returns the scale factor to apply for a given MDPI density to compute the - * absolute pixel count to use to draw an icon of the given target density - * - * @param density the density - * @return a factor to multiple mdpi distances with to compute the target density - */ - public static float getMdpiScaleFactor(Density density) { - return density.getDpiValue() / (float) Density.MEDIUM.getDpiValue(); - } - - /** - * Returns one of the built in stencil images, or null - * - * @param relativePath stencil path such as "launcher-stencil/square/web/back.png" - * @return the image, or null - * @throws IOException if an unexpected I/O error occurs - */ - @SuppressWarnings("resource") // Eclipse doesn't know about Closeables#closeQuietly yet - public static BufferedImage getStencilImage(String relativePath) throws IOException { - InputStream is = GraphicGenerator.class.getResourceAsStream(relativePath); - try { - return ImageIO.read(is); - } finally { - Closeables.closeQuietly(is); - } - } - - /** - * Returns the icon (32x32) for a given clip art image. - * - * @param name the name of the image to be loaded (which can be looked up via - * {@link #getClipartNames()}) - * @return the icon image - * @throws IOException if the image cannot be loaded - */ - @SuppressWarnings("resource") // Eclipse doesn't know about Closeables#closeQuietly yet - public static BufferedImage getClipartIcon(String name) throws IOException { - InputStream is = GraphicGenerator.class.getResourceAsStream( - "/images/clipart/small/" + name); - try { - return ImageIO.read(is); - } finally { - Closeables.closeQuietly(is); - } - } - - /** - * Returns the full size clip art image for a given image name. - * - * @param name the name of the image to be loaded (which can be looked up via - * {@link #getClipartNames()}) - * @return the clip art image - * @throws IOException if the image cannot be loaded - */ - @SuppressWarnings("resource") // Eclipse doesn't know about Closeables#closeQuietly yet - public static BufferedImage getClipartImage(String name) throws IOException { - InputStream is = GraphicGenerator.class.getResourceAsStream( - "/images/clipart/big/" + name); - try { - return ImageIO.read(is); - } finally { - Closeables.closeQuietly(is); - } - } - - /** - * Returns the names of available clip art images which can be obtained by passing the - * name to {@link #getClipartIcon(String)} or - * {@link GraphicGenerator#getClipartImage(String)} - * - * @return an iterator for the available image names - */ - public static Iterator<String> getClipartNames() { - List<String> names = new ArrayList<String>(80); - try { - String pathPrefix = "images/clipart/big/"; //$NON-NLS-1$ - ProtectionDomain protectionDomain = GraphicGenerator.class.getProtectionDomain(); - URL url = protectionDomain.getCodeSource().getLocation(); - File file; - try { - file = new File(url.toURI()); - } catch (URISyntaxException e) { - file = new File(url.getPath()); - } - final ZipFile zipFile = new JarFile(file); - Enumeration<? extends ZipEntry> enumeration = zipFile.entries(); - while (enumeration.hasMoreElements()) { - ZipEntry zipEntry = enumeration.nextElement(); - String name = zipEntry.getName(); - if (!name.startsWith(pathPrefix) || !name.endsWith(".png")) { //$NON-NLS-1$ - continue; - } - - int lastSlash = name.lastIndexOf('/'); - if (lastSlash != -1) { - name = name.substring(lastSlash + 1); - } - names.add(name); - } - } catch (final Exception e) { - e.printStackTrace(); - } - - return names.iterator(); - } -} diff --git a/assetstudio/src/com/android/assetstudiolib/GraphicGeneratorContext.java b/assetstudio/src/com/android/assetstudiolib/GraphicGeneratorContext.java deleted file mode 100644 index e0b00a6..0000000 --- a/assetstudio/src/com/android/assetstudiolib/GraphicGeneratorContext.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.assetstudiolib; - -import java.awt.image.BufferedImage; - -/** - * The context used during graphic generation. - */ -public interface GraphicGeneratorContext { - /** - * Loads the given image resource, as requested by the graphic generator. - * - * @param path The path to the resource, relative to the general "resources" path, as defined by - * the context implementor. - * @return The loaded image resource, or null if there was an error. - */ - public BufferedImage loadImageResource(String path); -} diff --git a/assetstudio/src/com/android/assetstudiolib/LauncherIconGenerator.java b/assetstudio/src/com/android/assetstudiolib/LauncherIconGenerator.java deleted file mode 100644 index 8902774..0000000 --- a/assetstudio/src/com/android/assetstudiolib/LauncherIconGenerator.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 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.assetstudiolib; - -import com.android.resources.Density; -import com.android.utils.Pair; - -import java.awt.AlphaComposite; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.image.BufferedImage; -import java.util.HashMap; -import java.util.Map; - -/** - * A {@link GraphicGenerator} that generates Android "launcher" icons. - */ -public class LauncherIconGenerator extends GraphicGenerator { - private static final Rectangle IMAGE_SIZE_WEB = new Rectangle(0, 0, 512, 512); - private static final Rectangle IMAGE_SIZE_MDPI = new Rectangle(0, 0, 48, 48); - - private static final Map<Pair<Shape, Density>, Rectangle> TARGET_RECTS - = new HashMap<Pair<Shape, Density>, Rectangle>(); - - static { - // None, Web - TARGET_RECTS.put(Pair.of(Shape.NONE, (Density) null), new Rectangle(32, 32, 448, 448)); - // None, HDPI - TARGET_RECTS.put(Pair.of(Shape.NONE, Density.HIGH), new Rectangle(4, 4, 64, 64)); - // None, MDPI - TARGET_RECTS.put(Pair.of(Shape.NONE, Density.MEDIUM), new Rectangle(3, 3, 42, 42)); - - // Circle, Web - TARGET_RECTS.put(Pair.of(Shape.CIRCLE, (Density) null), new Rectangle(32, 43, 448, 448)); - // Circle, HDPI - TARGET_RECTS.put(Pair.of(Shape.CIRCLE, Density.HIGH), new Rectangle(4, 6, 64, 64)); - // Circle, MDPI - TARGET_RECTS.put(Pair.of(Shape.CIRCLE, Density.MEDIUM), new Rectangle(3, 4, 42, 42)); - - // Square, Web - TARGET_RECTS.put(Pair.of(Shape.SQUARE, (Density) null), new Rectangle(32, 53, 448, 427)); - // Square, HDPI - TARGET_RECTS.put(Pair.of(Shape.SQUARE, Density.HIGH), new Rectangle(4, 8, 64, 60)); - // Square, MDPI - TARGET_RECTS.put(Pair.of(Shape.SQUARE, Density.MEDIUM), new Rectangle(3, 5, 42, 40)); - } - - @Override - public BufferedImage generate(GraphicGeneratorContext context, Options options) { - LauncherOptions launcherOptions = (LauncherOptions) options; - - String density; - if (launcherOptions.isWebGraphic) { - density = "web"; - } else { - density = launcherOptions.density.getResourceValue(); - } - - BufferedImage backImage = null, foreImage = null, maskImage = null, maskInnerImage = null; - if (launcherOptions.shape != Shape.NONE && launcherOptions.shape != null) { - String shape = launcherOptions.shape.id; - backImage = context.loadImageResource("/images/launcher_stencil/" - + shape + "/" + density + "/back.png"); - foreImage = context.loadImageResource("/images/launcher_stencil/" - + shape + "/" + density + "/" + launcherOptions.style.id + ".png"); - maskImage = context.loadImageResource("/images/launcher_stencil/" - + shape + "/" + density + "/mask.png"); - maskInnerImage = context.loadImageResource("/images/launcher_stencil/" - + shape + "/" + density + "/mask_inner.png"); - } - - Rectangle imageRect = IMAGE_SIZE_WEB; - if (!launcherOptions.isWebGraphic) { - imageRect = Util.scaleRectangle(IMAGE_SIZE_MDPI, - GraphicGenerator.getMdpiScaleFactor(launcherOptions.density)); - } - - Rectangle targetRect = TARGET_RECTS.get( - Pair.of(launcherOptions.shape, launcherOptions.density)); - if (targetRect == null) { - // Scale up from MDPI if no density-specific target rectangle is defined. - targetRect = Util.scaleRectangle( - TARGET_RECTS.get(Pair.of(launcherOptions.shape, Density.MEDIUM)), - GraphicGenerator.getMdpiScaleFactor(launcherOptions.density)); - } - - BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); - Graphics2D g = (Graphics2D) outImage.getGraphics(); - if (backImage != null) { - g.drawImage(backImage, 0, 0, null); - } - - BufferedImage tempImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); - Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); - if (maskImage != null) { - g2.drawImage(maskImage, 0, 0, null); - g2.setComposite(AlphaComposite.SrcAtop); - g2.setPaint(new Color(launcherOptions.backgroundColor)); - g2.fillRect(0, 0, imageRect.width, imageRect.height); - } - - BufferedImage tempImage2 = Util.newArgbBufferedImage(imageRect.width, imageRect.height); - Graphics2D g3 = (Graphics2D) tempImage2.getGraphics(); - if (maskInnerImage != null) { - g3.drawImage(maskInnerImage, 0, 0, null); - g3.setComposite(AlphaComposite.SrcAtop); - g3.setPaint(new Color(launcherOptions.backgroundColor)); - g3.fillRect(0, 0, imageRect.width, imageRect.height); - } - - if (launcherOptions.crop) { - Util.drawCenterCrop(g3, launcherOptions.sourceImage, targetRect); - } else { - Util.drawCenterInside(g3, launcherOptions.sourceImage, targetRect); - } - - g2.drawImage(tempImage2, 0, 0, null); - g.drawImage(tempImage, 0, 0, null); - if (foreImage != null) { - g.drawImage(foreImage, 0, 0, null); - } - - g.dispose(); - g2.dispose(); - - return outImage; - } - - @Override - public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap, - GraphicGeneratorContext context, Options options, String name) { - LauncherOptions launcherOptions = (LauncherOptions) options; - boolean generateWebImage = launcherOptions.isWebGraphic; - launcherOptions.isWebGraphic = false; - super.generate(category, categoryMap, context, options, name); - - if (generateWebImage) { - launcherOptions.isWebGraphic = true; - launcherOptions.density = null; - BufferedImage image = generate(context, options); - if (image != null) { - Map<String, BufferedImage> imageMap = new HashMap<String, BufferedImage>(); - categoryMap.put("Web", imageMap); - imageMap.put(getIconPath(options, name), image); - } - } - } - - @Override - protected String getIconPath(Options options, String name) { - if (((LauncherOptions) options).isWebGraphic) { - return name + "-web.png"; // Store at the root of the project - } - - return super.getIconPath(options, name); - } - - /** Options specific to generating launcher icons */ - public static class LauncherOptions extends GraphicGenerator.Options { - /** Background color, as an RRGGBB packed integer */ - public int backgroundColor = 0; - - /** Whether the image should be cropped or not */ - public boolean crop = true; - - /** The shape to use for the background */ - public Shape shape = Shape.SQUARE; - - /** The effects to apply to the foreground */ - public Style style = Style.SIMPLE; - - /** - * Whether a web graphic should be generated (will ignore normal density - * setting). The {@link #generate(GraphicGeneratorContext, Options)} - * method will use this to decide whether to generate a normal density - * icon or a high res web image. The - * {@link GraphicGenerator#generate(String, Map, GraphicGeneratorContext, Options, String)} - * method will use this flag to determine whether it should include a - * web graphic in its iteration. - */ - public boolean isWebGraphic; - } -} diff --git a/assetstudio/src/com/android/assetstudiolib/MenuIconGenerator.java b/assetstudio/src/com/android/assetstudiolib/MenuIconGenerator.java deleted file mode 100644 index 33b9c34..0000000 --- a/assetstudio/src/com/android/assetstudiolib/MenuIconGenerator.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.assetstudiolib; - -import com.android.assetstudiolib.Util.Effect; -import com.android.assetstudiolib.Util.FillEffect; -import com.android.assetstudiolib.Util.ShadowEffect; - -import java.awt.Color; -import java.awt.GradientPaint; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.image.BufferedImage; - -/** - * A {@link GraphicGenerator} that generates Android "menu" icons. - */ -public class MenuIconGenerator extends GraphicGenerator { - /** Creates a menu icon generator */ - public MenuIconGenerator() { - } - - @Override - public BufferedImage generate(GraphicGeneratorContext context, Options options) { - Rectangle imageSizeHdpi = new Rectangle(0, 0, 48, 48); - Rectangle targetRectHdpi = new Rectangle(8, 8, 32, 32); - float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density); - Rectangle imageRect = Util.scaleRectangle(imageSizeHdpi, scaleFactor); - Rectangle targetRect = Util.scaleRectangle(targetRectHdpi, scaleFactor); - - BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); - Graphics2D g = (Graphics2D) outImage.getGraphics(); - - BufferedImage tempImage = Util.newArgbBufferedImage( - imageRect.width, imageRect.height); - Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); - Util.drawCenterInside(g2, options.sourceImage, targetRect); - - Util.drawEffects(g, tempImage, 0, 0, new Effect[] { - new FillEffect( - new GradientPaint( - 0, 0, - new Color(0xa3a3a3), - 0, imageRect.height, - new Color(0x787878))), - new ShadowEffect( - 0, - 2 * scaleFactor, - 2 * scaleFactor, - Color.BLACK, - 0.2, - true), - new ShadowEffect( - 0, - 1, - 0, - Color.BLACK, - 0.35, - true), - new ShadowEffect( - 0, - -1, - 0, - Color.WHITE, - 0.35, - true), - }); - - g.dispose(); - g2.dispose(); - - return outImage; - } -} diff --git a/assetstudio/src/com/android/assetstudiolib/NotificationIconGenerator.java b/assetstudio/src/com/android/assetstudiolib/NotificationIconGenerator.java deleted file mode 100644 index b84af1b..0000000 --- a/assetstudio/src/com/android/assetstudiolib/NotificationIconGenerator.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.assetstudiolib; - -import com.android.assetstudiolib.Util.Effect; -import com.android.assetstudiolib.Util.FillEffect; -import com.android.assetstudiolib.Util.ShadowEffect; - -import java.awt.Color; -import java.awt.GradientPaint; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.image.BufferedImage; -import java.util.Map; - -/** - * Generate icons for the notifications bar - */ -public class NotificationIconGenerator extends GraphicGenerator { - /** Creates a new {@link NotificationIconGenerator} */ - public NotificationIconGenerator() { - } - - @Override - public BufferedImage generate(GraphicGeneratorContext context, Options options) { - Rectangle iconSizeMdpi; - Rectangle targetRectMdpi; - NotificationOptions notificationOptions = (NotificationOptions) options; - if (notificationOptions.version == Version.OLDER) { - iconSizeMdpi = new Rectangle(0, 0, 25, 25); - targetRectMdpi = new Rectangle(4, 4, 17, 17); - } else if (notificationOptions.version == Version.V11) { - iconSizeMdpi = new Rectangle(0, 0, 24, 24); - targetRectMdpi = new Rectangle(1, 1, 22, 22); - } else { - assert notificationOptions.version == Version.V9; - iconSizeMdpi = new Rectangle(0, 0, 16, 25); - targetRectMdpi = new Rectangle(0, 5, 16, 16); - } - - final float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density); - Rectangle imageRect = Util.scaleRectangle(iconSizeMdpi, scaleFactor); - Rectangle targetRect = Util.scaleRectangle(targetRectMdpi, scaleFactor); - - BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); - Graphics2D g = (Graphics2D) outImage.getGraphics(); - - BufferedImage tempImage = Util.newArgbBufferedImage( - imageRect.width, imageRect.height); - Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); - - if (notificationOptions.version == Version.OLDER) { - BufferedImage backImage = context.loadImageResource( - "/images/notification_stencil/" - + notificationOptions.density.getResourceValue() - + ".png"); - g.drawImage(backImage, 0, 0, null); - BufferedImage top = options.sourceImage; - BufferedImage filled = Util.filledImage(top, Color.WHITE); - Util.drawCenterInside(g, filled, targetRect); - } else if (notificationOptions.version == Version.V11) { - Util.drawCenterInside(g2, options.sourceImage, targetRect); - Util.drawEffects(g, tempImage, 0, 0, new Effect[] { - new FillEffect(Color.WHITE), - }); - } else { - assert notificationOptions.version == Version.V9; - Util.drawCenterInside(g2, options.sourceImage, targetRect); - Util.drawEffects(g, tempImage, 0, 0, new Effect[] { - new FillEffect( - new GradientPaint( - 0, 0, - new Color(0x919191), - 0, imageRect.height, - new Color(0x828282))), - new ShadowEffect( - 0, - 1, - 0, - Color.WHITE, - 0.10, - true), - }); - } - - g.dispose(); - g2.dispose(); - - return outImage; - } - - @Override - public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap, - GraphicGeneratorContext context, Options baseOptions, String name) { - NotificationOptions options = (NotificationOptions) baseOptions; - if (options.minSdk < 9) { - options.version = Version.OLDER; - super.generate(options.version.getDisplayName(), categoryMap, context, options, name); - } - if (options.minSdk < 11) { - options.version = Version.V9; - super.generate(options.version.getDisplayName(), categoryMap, context, options, name); - } - options.version = Version.V11; - super.generate(options.minSdk < 11 ? options.version.getDisplayName() : null, - categoryMap, context, options, name); - } - - @Override - protected String getIconFolder(Options options) { - String folder = super.getIconFolder(options); - Version version = ((NotificationOptions) options).version; - if (version == Version.V11 && options.minSdk < 11) { - return folder + "-v11"; //$NON-NLS-1$ - } else if (version == Version.V9 && options.minSdk < 9) { - return folder + "-v9"; //$NON-NLS-1$ - } else { - return folder; - } - } - - /** - * Options specific to generating notification icons - */ - public static class NotificationOptions extends GraphicGenerator.Options { - /** - * The version of the icon to generate - different styles are used for different - * versions of Android - */ - public Version version = Version.V9; - } - - /** - * The version of the icon to generate - different styles are used for different - * versions of Android - */ - public enum Version { - /** Icon style used for -v9 and -v10 */ - V9("V9"), - - /** Icon style used for -v11 (Honeycomb) and later */ - V11("V11"), - - /** Icon style used for versions older than v9 */ - OLDER("Other"); - - private final String mDisplayName; - - Version(String displayName) { - mDisplayName = displayName; - } - - /** - * Returns the display name for this version, typically shown as a - * category - * - * @return the display name, never null - */ - public String getDisplayName() { - return mDisplayName; - } - } -} diff --git a/assetstudio/src/com/android/assetstudiolib/TabIconGenerator.java b/assetstudio/src/com/android/assetstudiolib/TabIconGenerator.java deleted file mode 100644 index 3d2ac30..0000000 --- a/assetstudio/src/com/android/assetstudiolib/TabIconGenerator.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.assetstudiolib; - -import com.android.assetstudiolib.Util.Effect; -import com.android.assetstudiolib.Util.FillEffect; -import com.android.assetstudiolib.Util.ShadowEffect; - -import java.awt.Color; -import java.awt.GradientPaint; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.image.BufferedImage; -import java.util.Map; - - -/** - * Generate icons for tabs - */ -public class TabIconGenerator extends GraphicGenerator { - /** Creates a new {@link TabIconGenerator} */ - public TabIconGenerator() { - } - - @Override - public BufferedImage generate(GraphicGeneratorContext context, Options options) { - Rectangle iconSizeMdpi = new Rectangle(0, 0, 32, 32); - Rectangle targetRectMdpi = new Rectangle(2, 2, 28, 28); - final float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density); - Rectangle imageRect = Util.scaleRectangle(iconSizeMdpi, scaleFactor); - Rectangle targetRect = Util.scaleRectangle(targetRectMdpi, scaleFactor); - BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); - Graphics2D g = (Graphics2D) outImage.getGraphics(); - - BufferedImage tempImage = Util.newArgbBufferedImage( - imageRect.width, imageRect.height); - Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); - Util.drawCenterInside(g2, options.sourceImage, targetRect); - - TabOptions tabOptions = (TabOptions) options; - if (tabOptions.selected) { - if (tabOptions.oldStyle) { - Util.drawEffects(g, tempImage, 0, 0, new Effect[] { - new FillEffect( - new GradientPaint( - 0, 0, - new Color(0xa3a3a3), - 0, imageRect.height, - new Color(0x787878))), - new ShadowEffect( - 0, - 2 * scaleFactor, - 2 * scaleFactor, - Color.BLACK, - 0.2, - true), - new ShadowEffect( - 0, - 1, - 0, - Color.BLACK, - 0.35, - true), - new ShadowEffect( - 0, - -1, - 0, - Color.WHITE, - 0.35, - true), - }); - } else { - Util.drawEffects(g, tempImage, 0, 0, new Effect[] { - new FillEffect(Color.WHITE), - new ShadowEffect( - 0, - 0, - 3 * scaleFactor, - Color.BLACK, - 0.25, - false), - }); - } - } else { - // Unselected - if (tabOptions.oldStyle) { - Util.drawEffects(g, tempImage, 0, 0, new Effect[] { - new FillEffect( - new GradientPaint( - 0, 0.25f * imageRect.height, - new Color(0xf9f9f9), - 0, imageRect.height, - new Color(0xdfdfdf))), - new ShadowEffect( - 0, - 2 * scaleFactor, - 2 * scaleFactor, - Color.BLACK, - 0.1, - true), - new ShadowEffect( - 0, - 1, - 0, - Color.BLACK, - 0.35, - true), - new ShadowEffect( - 0, - -1, - 0, - Color.WHITE, - 0.35, - true), - }); - } else { - Util.drawEffects(g, tempImage, 0, 0, new Effect[] { - new FillEffect(new Color(0x808080)), - }); - } - } - - g.dispose(); - g2.dispose(); - - return outImage; - } - - @Override - public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap, - GraphicGeneratorContext context, Options baseOptions, String name) { - TabOptions options = (TabOptions) baseOptions; - // Generate all permutations of tabOptions.selected and tabOptions.oldStyle - options.selected = true; - options.oldStyle = false; - - String selectedLabelV5 = "Selected (v5+)"; - String unselectedLabelV5 = "Unselected (v5+)"; - String selectedLabel = "Selected"; - String unselectedLabel = "Unselected"; - - boolean generateOldStyle = options.minSdk < 5; - if (generateOldStyle) { - options.oldStyle = true; - options.selected = true; - super.generate(selectedLabel, categoryMap, context, options, name); - options.selected = false; - super.generate(unselectedLabel, categoryMap, context, options, name); - } - - options.oldStyle = false; - options.selected = true; - super.generate(generateOldStyle ? unselectedLabelV5 : unselectedLabel, - categoryMap, context, options, name); - options.selected = false; - super.generate(generateOldStyle ? selectedLabelV5 : selectedLabel, - categoryMap, context, options, name); - } - - @Override - protected String getIconFolder(Options options) { - String folder = super.getIconFolder(options); - - TabOptions tabOptions = (TabOptions) options; - if (tabOptions.oldStyle || options.minSdk >= 5) { - return folder; - } else { - return folder + "-v5"; //$NON-NLS-1$ - } - } - - @Override - protected String getIconName(Options options, String name) { - TabOptions tabOptions = (TabOptions) options; - if (tabOptions.selected) { - return name + "_selected.png"; //$NON-NLS-1$ - } else { - return name + "_unselected.png"; //$NON-NLS-1$ - } - } - - /** Options specific to generating tab icons */ - public static class TabOptions extends GraphicGenerator.Options { - /** Generate icon in the style used prior to v5 */ - public boolean oldStyle; - /** Generate "selected" icon if true, and "unselected" icon if false */ - public boolean selected = true; - } -} diff --git a/assetstudio/src/com/android/assetstudiolib/TextRenderUtil.java b/assetstudio/src/com/android/assetstudiolib/TextRenderUtil.java deleted file mode 100644 index e08a234..0000000 --- a/assetstudio/src/com/android/assetstudiolib/TextRenderUtil.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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.assetstudiolib; - -import java.awt.Color; -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.font.FontRenderContext; -import java.awt.font.TextLayout; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; - -/** - * A set of utility classes for rendering text to a {@link BufferedImage}, suitable for use as a - * source image to {@link GraphicGenerator} objects. - */ -public class TextRenderUtil { - /** - * Renders the given string with the provided {@link Options} to a - * {@link BufferedImage}. - * - * @param text The text to render. - * @param paddingPercentage If nonzero, a percentage of the width or height - * (whichever is smaller) to add as padding around the text - * @param options The optional parameters for rendering the text. - * @return An image, suitable for use as an input to a - * {@link GraphicGenerator}. - */ - public static BufferedImage renderTextImage(String text, int paddingPercentage, - Options options) { - if (options == null) { - options = new Options(); - } - - BufferedImage tempImage = Util.newArgbBufferedImage(1, 1); - if (text == null || text.equals("")) { - return tempImage; - } - - Graphics2D tempG = (Graphics2D) tempImage.getGraphics(); - - Font font = options.font; - if (font == null) { - font = new Font(options.fontName, options.fontStyle, options.fontSize); - // Map<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>(); - // map.put(TextAttribute.TRACKING, 0.3); - // font = font.deriveFont(map); - } - - FontRenderContext frc = tempG.getFontRenderContext(); - - TextLayout layout = new TextLayout(text, font, frc); - Rectangle2D bounds = layout.getBounds(); - - // The padding is a percentage relative to the overall minimum of the width or height - if (paddingPercentage != 0) { - double minDimension = Math.min(bounds.getWidth(), bounds.getHeight()); - double delta = minDimension * paddingPercentage / 100; - bounds.setRect(bounds.getMinX() - delta, bounds.getMinY() - delta, - bounds.getWidth() + 2 * delta, bounds.getHeight() + 2 * delta); - } - - BufferedImage image = Util.newArgbBufferedImage( - Math.max(1, (int) bounds.getWidth()), Math.max(1, (int) bounds.getHeight())); - Graphics2D g = (Graphics2D) image.getGraphics(); - g.setColor(new Color(options.foregroundColor, true)); - g.setFont(font); - - g.setRenderingHint( - RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setRenderingHint( - RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - - g.drawString(text, (float) -bounds.getX(), (float) -bounds.getY()); - - g.dispose(); - tempG.dispose(); - - return image; - } - - /** - * The parameters for text rendering. There are no required values so a <code>new - * Options()</code> object is considered valid. - */ - public static class Options { - // We use a large default font size to reduce the need to scale generated images up. - // TODO: Instead, a graphic generator should use a different source image for each density. - private static final int DEFAULT_FONT_SIZE = 512; - - /** Foreground color to render text with, as an AARRGGBB packed integer */ - public int foregroundColor = 0xFFFFFFFF; - - /** - * The optional {@link Font} to use. If null, a {@link Font} object will be generated using - * the other options. - */ - public Font font = null; - - /** - * The optional font name. Defaults to {@link Font#SERIF}. - * - * @see Font#Font(String, int, int) - */ - public String fontName = Font.SERIF; - - /** - * The optional font styling (bold and/or italic). Defaults to no styling. - * - * @see Font#Font(String, int, int) - */ - public int fontStyle = 0; - - /** - * The optional font size, in points. Defaults to a very large font size, to prevent - * up-scaling rendered text. - * - * @see Font#Font(String, int, int) - */ - public int fontSize = DEFAULT_FONT_SIZE; - } -} diff --git a/assetstudio/src/com/android/assetstudiolib/Util.java b/assetstudio/src/com/android/assetstudiolib/Util.java deleted file mode 100644 index ee2a5f7..0000000 --- a/assetstudio/src/com/android/assetstudiolib/Util.java +++ /dev/null @@ -1,451 +0,0 @@ -/* - * 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.assetstudiolib; - -import java.awt.AlphaComposite; -import java.awt.Color; -import java.awt.Composite; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.Paint; -import java.awt.Rectangle; -import java.awt.image.BufferedImage; -import java.awt.image.BufferedImageOp; -import java.awt.image.ConvolveOp; -import java.awt.image.Kernel; -import java.awt.image.Raster; -import java.awt.image.RescaleOp; -import java.util.ArrayList; -import java.util.List; - -/** - * A set of utility classes for manipulating {@link BufferedImage} objects and drawing them to - * {@link Graphics2D} canvases. - */ -public class Util { - /** - * Scales the given rectangle by the given scale factor. - * - * @param rect The rectangle to scale. - * @param scaleFactor The factor to scale by. - * @return The scaled rectangle. - */ - public static Rectangle scaleRectangle(Rectangle rect, float scaleFactor) { - return new Rectangle( - (int) Math.round(rect.x * scaleFactor), - (int) Math.round(rect.y * scaleFactor), - (int) Math.round(rect.width * scaleFactor), - (int) Math.round(rect.height * scaleFactor)); - } - - /** - * Creates a new ARGB {@link BufferedImage} of the given width and height. - * - * @param width The width of the new image. - * @param height The height of the new image. - * @return The newly created image. - */ - public static BufferedImage newArgbBufferedImage(int width, int height) { - return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - } - - /** - * Smoothly scales the given {@link BufferedImage} to the given width and height using the - * {@link Image#SCALE_SMOOTH} algorithm (generally bicubic resampling or bilinear filtering). - * - * @param source The source image. - * @param width The destination width to scale to. - * @param height The destination height to scale to. - * @return A new, scaled image. - */ - public static BufferedImage scaledImage(BufferedImage source, int width, int height) { - Image scaledImage = source.getScaledInstance(width, height, Image.SCALE_SMOOTH); - BufferedImage scaledBufImage = new BufferedImage(width, height, - BufferedImage.TYPE_INT_ARGB); - Graphics g = scaledBufImage.createGraphics(); - g.drawImage(scaledImage, 0, 0, null); - g.dispose(); - return scaledBufImage; - } - - /** - * Applies a gaussian blur of the given radius to the given {@link BufferedImage} using a kernel - * convolution. - * - * @param source The source image. - * @param radius The blur radius, in pixels. - * @return A new, blurred image, or the source image if no blur is performed. - */ - public static BufferedImage blurredImage(BufferedImage source, double radius) { - if (radius == 0) { - return source; - } - - final int r = (int) Math.ceil(radius); - final int rows = r * 2 + 1; - final float[] kernelData = new float[rows * rows]; - - final double sigma = radius / 3; - final double sigma22 = 2 * sigma * sigma; - final double sqrtPiSigma22 = Math.sqrt(Math.PI * sigma22); - final double radius2 = radius * radius; - - double total = 0; - int index = 0; - double distance2; - - int x, y; - for (y = -r; y <= r; y++) { - for (x = -r; x <= r; x++) { - distance2 = 1.0 * x * x + 1.0 * y * y; - if (distance2 > radius2) { - kernelData[index] = 0; - } else { - kernelData[index] = (float) (Math.exp(-distance2 / sigma22) / sqrtPiSigma22); - } - total += kernelData[index]; - ++index; - } - } - - for (index = 0; index < kernelData.length; index++) { - kernelData[index] /= total; - } - - // We first pad the image so the kernel can operate at the edges. - BufferedImage paddedSource = paddedImage(source, r); - BufferedImage blurredPaddedImage = operatedImage(paddedSource, new ConvolveOp( - new Kernel(rows, rows, kernelData), ConvolveOp.EDGE_ZERO_FILL, null)); - return blurredPaddedImage.getSubimage(r, r, source.getWidth(), source.getHeight()); - } - - /** - * Inverts the alpha channel of the given {@link BufferedImage}. RGB data for the inverted area - * are undefined, so it's generally best to fill the resulting image with a color. - * - * @param source The source image. - * @return A new image with an alpha channel inverted from the original. - */ - public static BufferedImage invertedAlphaImage(BufferedImage source) { - final float[] scaleFactors = new float[]{1, 1, 1, -1}; - final float[] offsets = new float[]{0, 0, 0, 255}; - - return operatedImage(source, new RescaleOp(scaleFactors, offsets, null)); - } - - /** - * Applies a {@link BufferedImageOp} on the given {@link BufferedImage}. - * - * @param source The source image. - * @param op The operation to perform. - * @return A new image with the operation performed. - */ - public static BufferedImage operatedImage(BufferedImage source, BufferedImageOp op) { - BufferedImage newImage = newArgbBufferedImage(source.getWidth(), source.getHeight()); - Graphics2D g = (Graphics2D) newImage.getGraphics(); - g.drawImage(source, op, 0, 0); - return newImage; - } - - /** - * Fills the given {@link BufferedImage} with a {@link Paint}, preserving its alpha channel. - * - * @param source The source image. - * @param paint The paint to fill with. - * @return A new, painted/filled image. - */ - public static BufferedImage filledImage(BufferedImage source, Paint paint) { - BufferedImage newImage = newArgbBufferedImage(source.getWidth(), source.getHeight()); - Graphics2D g = (Graphics2D) newImage.getGraphics(); - g.drawImage(source, 0, 0, null); - g.setComposite(AlphaComposite.SrcAtop); - g.setPaint(paint); - g.fillRect(0, 0, source.getWidth(), source.getHeight()); - return newImage; - } - - /** - * Pads the given {@link BufferedImage} on all sides by the given padding amount. - * - * @param source The source image. - * @param padding The amount to pad on all sides, in pixels. - * @return A new, padded image, or the source image if no padding is performed. - */ - public static BufferedImage paddedImage(BufferedImage source, int padding) { - if (padding == 0) { - return source; - } - - BufferedImage newImage = newArgbBufferedImage( - source.getWidth() + padding * 2, source.getHeight() + padding * 2); - Graphics2D g = (Graphics2D) newImage.getGraphics(); - g.drawImage(source, padding, padding, null); - return newImage; - } - - /** - * Trims the transparent pixels from the given {@link BufferedImage} (returns a sub-image). - * - * @param source The source image. - * @return A new, trimmed image, or the source image if no trim is performed. - */ - public static BufferedImage trimmedImage(BufferedImage source) { - final int minAlpha = 1; - final int srcWidth = source.getWidth(); - final int srcHeight = source.getHeight(); - Raster raster = source.getRaster(); - int l = srcWidth, t = srcHeight, r = 0, b = 0; - - int alpha, x, y; - int[] pixel = new int[4]; - for (y = 0; y < srcHeight; y++) { - for (x = 0; x < srcWidth; x++) { - raster.getPixel(x, y, pixel); - alpha = pixel[3]; - if (alpha >= minAlpha) { - l = Math.min(x, l); - t = Math.min(y, t); - r = Math.max(x, r); - b = Math.max(y, b); - } - } - } - - if (l > r || t > b) { - // No pixels, couldn't trim - return source; - } - - return source.getSubimage(l, t, r - l + 1, b - t + 1); - } - - /** - * Draws the given {@link BufferedImage} to the canvas, at the given coordinates, with the given - * {@link Effect}s applied. Note that drawn effects may be outside the bounds of the source - * image. - * - * @param g The destination canvas. - * @param source The source image. - * @param x The x offset at which to draw the image. - * @param y The y offset at which to draw the image. - * @param effects The list of effects to apply. - */ - public static void drawEffects(Graphics2D g, BufferedImage source, int x, int y, - Effect[] effects) { - List<ShadowEffect> shadowEffects = new ArrayList<ShadowEffect>(); - List<FillEffect> fillEffects = new ArrayList<FillEffect>(); - - for (Effect effect : effects) { - if (effect instanceof ShadowEffect) { - shadowEffects.add((ShadowEffect) effect); - } else if (effect instanceof FillEffect) { - fillEffects.add((FillEffect) effect); - } - } - - Composite oldComposite = g.getComposite(); - for (ShadowEffect effect : shadowEffects) { - if (effect.inner) { - continue; - } - - // Outer shadow - g.setComposite(AlphaComposite.getInstance( - AlphaComposite.SRC_OVER, (float) effect.opacity)); - g.drawImage( - filledImage( - blurredImage(source, effect.radius), - effect.color), - (int) effect.xOffset, (int) effect.yOffset, null); - } - g.setComposite(oldComposite); - - // Inner shadow & fill effects. - final Rectangle imageRect = new Rectangle(0, 0, source.getWidth(), source.getHeight()); - BufferedImage out = newArgbBufferedImage(imageRect.width, imageRect.height); - Graphics2D g2 = (Graphics2D) out.getGraphics(); - double fillOpacity = 1.0; - - g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); - g2.drawImage(source, 0, 0, null); - g2.setComposite(AlphaComposite.SrcAtop); - - // Gradient fill - for (FillEffect effect : fillEffects) { - g2.setPaint(effect.paint); - g2.fillRect(0, 0, imageRect.width, imageRect.height); - fillOpacity = Math.max(0, Math.min(1, effect.opacity)); - } - - // Inner shadows - for (ShadowEffect effect : shadowEffects) { - if (!effect.inner) { - continue; - } - - BufferedImage innerShadowImage = newArgbBufferedImage( - imageRect.width, imageRect.height); - Graphics2D g3 = (Graphics2D) innerShadowImage.getGraphics(); - g3.drawImage(source, (int) effect.xOffset, (int) effect.yOffset, null); - g2.setComposite(AlphaComposite.getInstance( - AlphaComposite.SRC_ATOP, (float) effect.opacity)); - g2.drawImage( - filledImage( - blurredImage(invertedAlphaImage(innerShadowImage), effect.radius), - effect.color), - 0, 0, null); - } - - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) fillOpacity)); - g.drawImage(out, x, y, null); - g.setComposite(oldComposite); - } - - /** - * Draws the given {@link BufferedImage} to the canvas, centered, wholly contained within the - * bounds defined by the destination rectangle, and with preserved aspect ratio. - * - * @param g The destination canvas. - * @param source The source image. - * @param dstRect The destination rectangle in the destination canvas into which to draw the - * image. - */ - public static void drawCenterInside(Graphics2D g, BufferedImage source, Rectangle dstRect) { - final int srcWidth = source.getWidth(); - final int srcHeight = source.getHeight(); - if (srcWidth * 1.0 / srcHeight > dstRect.width * 1.0 / dstRect.height) { - final int scaledWidth = Math.max(1, dstRect.width); - final int scaledHeight = Math.max(1, dstRect.width * srcHeight / srcWidth); - Image scaledImage = scaledImage(source, scaledWidth, scaledHeight); - g.drawImage(scaledImage, - dstRect.x, - dstRect.y + (dstRect.height - scaledHeight) / 2, - dstRect.x + dstRect.width, - dstRect.y + (dstRect.height - scaledHeight) / 2 + scaledHeight, - 0, - 0, - 0 + scaledWidth, - 0 + scaledHeight, - null); - } else { - final int scaledWidth = Math.max(1, dstRect.height * srcWidth / srcHeight); - final int scaledHeight = Math.max(1, dstRect.height); - Image scaledImage = scaledImage(source, scaledWidth, scaledHeight); - g.drawImage(scaledImage, - dstRect.x + (dstRect.width - scaledWidth) / 2, - dstRect.y, - dstRect.x + (dstRect.width - scaledWidth) / 2 + scaledWidth, - dstRect.y + dstRect.height, - 0, - 0, - 0 + scaledWidth, - 0 + scaledHeight, - null); - } - } - - /** - * Draws the given {@link BufferedImage} to the canvas, centered and cropped to fill the - * bounds defined by the destination rectangle, and with preserved aspect ratio. - * - * @param g The destination canvas. - * @param source The source image. - * @param dstRect The destination rectangle in the destination canvas into which to draw the - * image. - */ - public static void drawCenterCrop(Graphics2D g, BufferedImage source, Rectangle dstRect) { - final int srcWidth = source.getWidth(); - final int srcHeight = source.getHeight(); - if (srcWidth * 1.0 / srcHeight > dstRect.width * 1.0 / dstRect.height) { - final int scaledWidth = dstRect.height * srcWidth / srcHeight; - final int scaledHeight = dstRect.height; - Image scaledImage = scaledImage(source, scaledWidth, scaledHeight); - g.drawImage(scaledImage, - dstRect.x, - dstRect.y, - dstRect.x + dstRect.width, - dstRect.y + dstRect.height, - 0 + (scaledWidth - dstRect.width) / 2, - 0, - 0 + (scaledWidth - dstRect.width) / 2 + dstRect.width, - 0 + dstRect.height, - null); - } else { - final int scaledWidth = dstRect.width; - final int scaledHeight = dstRect.width * srcHeight / srcWidth; - Image scaledImage = scaledImage(source, scaledWidth, scaledHeight); - g.drawImage(scaledImage, - dstRect.x, - dstRect.y, - dstRect.x + dstRect.width, - dstRect.y + dstRect.height, - 0, - 0 + (scaledHeight - dstRect.height) / 2, - 0 + dstRect.width, - 0 + (scaledHeight - dstRect.height) / 2 + dstRect.height, - null); - } - } - - /** - * An effect to apply in - * {@link Util#drawEffects(java.awt.Graphics2D, java.awt.image.BufferedImage, int, int, Util.Effect[])} - */ - public static abstract class Effect { - } - - /** - * An inner or outer shadow. - */ - public static class ShadowEffect extends Effect { - public double xOffset; - public double yOffset; - public double radius; - public Color color; - public double opacity; - public boolean inner; - - public ShadowEffect(double xOffset, double yOffset, double radius, Color color, - double opacity, boolean inner) { - this.xOffset = xOffset; - this.yOffset = yOffset; - this.radius = radius; - this.color = color; - this.opacity = opacity; - this.inner = inner; - } - } - - /** - * A fill, defined by a paint. - */ - public static class FillEffect extends Effect { - public Paint paint; - public double opacity; - - public FillEffect(Paint paint, double opacity) { - this.paint = paint; - this.opacity = opacity; - } - - public FillEffect(Paint paint) { - this.paint = paint; - this.opacity = 1.0; - } - } -} diff --git a/assetstudio/src/images/clipart/big/1-navigation-accept.png b/assetstudio/src/images/clipart/big/1-navigation-accept.png Binary files differdeleted file mode 100644 index 121b347..0000000 --- a/assetstudio/src/images/clipart/big/1-navigation-accept.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/1-navigation-back.png b/assetstudio/src/images/clipart/big/1-navigation-back.png Binary files differdeleted file mode 100644 index 863074c..0000000 --- a/assetstudio/src/images/clipart/big/1-navigation-back.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/1-navigation-cancel.png b/assetstudio/src/images/clipart/big/1-navigation-cancel.png Binary files differdeleted file mode 100644 index d968d34..0000000 --- a/assetstudio/src/images/clipart/big/1-navigation-cancel.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/1-navigation-collapse.png b/assetstudio/src/images/clipart/big/1-navigation-collapse.png Binary files differdeleted file mode 100644 index e525983..0000000 --- a/assetstudio/src/images/clipart/big/1-navigation-collapse.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/1-navigation-expand.png b/assetstudio/src/images/clipart/big/1-navigation-expand.png Binary files differdeleted file mode 100644 index f5b0728..0000000 --- a/assetstudio/src/images/clipart/big/1-navigation-expand.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/1-navigation-forward.png b/assetstudio/src/images/clipart/big/1-navigation-forward.png Binary files differdeleted file mode 100644 index 4cae802..0000000 --- a/assetstudio/src/images/clipart/big/1-navigation-forward.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/1-navigation-next-item.png b/assetstudio/src/images/clipart/big/1-navigation-next-item.png Binary files differdeleted file mode 100644 index a1b8b83..0000000 --- a/assetstudio/src/images/clipart/big/1-navigation-next-item.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/1-navigation-previous-item.png b/assetstudio/src/images/clipart/big/1-navigation-previous-item.png Binary files differdeleted file mode 100644 index 9312bf6..0000000 --- a/assetstudio/src/images/clipart/big/1-navigation-previous-item.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/1-navigation-refresh.png b/assetstudio/src/images/clipart/big/1-navigation-refresh.png Binary files differdeleted file mode 100644 index b5202f9..0000000 --- a/assetstudio/src/images/clipart/big/1-navigation-refresh.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-accounts.png b/assetstudio/src/images/clipart/big/10-device-access-accounts.png Binary files differdeleted file mode 100644 index 64544c5..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-accounts.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-add-alarm.png b/assetstudio/src/images/clipart/big/10-device-access-add-alarm.png Binary files differdeleted file mode 100644 index bd4bcc3..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-add-alarm.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-alarms.png b/assetstudio/src/images/clipart/big/10-device-access-alarms.png Binary files differdeleted file mode 100644 index a5b1ead..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-alarms.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-battery.png b/assetstudio/src/images/clipart/big/10-device-access-battery.png Binary files differdeleted file mode 100644 index d86b2c1..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-battery.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-bightness-low.png b/assetstudio/src/images/clipart/big/10-device-access-bightness-low.png Binary files differdeleted file mode 100644 index 738f203..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-bightness-low.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-bluetooth-connected.png b/assetstudio/src/images/clipart/big/10-device-access-bluetooth-connected.png Binary files differdeleted file mode 100644 index 403a0b5..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-bluetooth-connected.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-bluetooth-searching.png b/assetstudio/src/images/clipart/big/10-device-access-bluetooth-searching.png Binary files differdeleted file mode 100644 index a99f65a..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-bluetooth-searching.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-bluetooth.png b/assetstudio/src/images/clipart/big/10-device-access-bluetooth.png Binary files differdeleted file mode 100644 index 556499d..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-bluetooth.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-brightness-auto.png b/assetstudio/src/images/clipart/big/10-device-access-brightness-auto.png Binary files differdeleted file mode 100644 index 46d2b8a..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-brightness-auto.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-brightness-high.png b/assetstudio/src/images/clipart/big/10-device-access-brightness-high.png Binary files differdeleted file mode 100644 index 97e3f19..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-brightness-high.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-brightness-medium.png b/assetstudio/src/images/clipart/big/10-device-access-brightness-medium.png Binary files differdeleted file mode 100644 index 5e361cb..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-brightness-medium.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-call.png b/assetstudio/src/images/clipart/big/10-device-access-call.png Binary files differdeleted file mode 100644 index 940bcb6..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-call.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-camera.png b/assetstudio/src/images/clipart/big/10-device-access-camera.png Binary files differdeleted file mode 100644 index ad8857a..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-camera.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-data-usage.png b/assetstudio/src/images/clipart/big/10-device-access-data-usage.png Binary files differdeleted file mode 100644 index 9fa73a5..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-data-usage.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-dial-pad.png b/assetstudio/src/images/clipart/big/10-device-access-dial-pad.png Binary files differdeleted file mode 100644 index 81da080..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-dial-pad.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-end-call.png b/assetstudio/src/images/clipart/big/10-device-access-end-call.png Binary files differdeleted file mode 100644 index c28f284..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-end-call.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-flash-automatic.png b/assetstudio/src/images/clipart/big/10-device-access-flash-automatic.png Binary files differdeleted file mode 100644 index e5c2c03..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-flash-automatic.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-flash-off.png b/assetstudio/src/images/clipart/big/10-device-access-flash-off.png Binary files differdeleted file mode 100644 index dfcb747..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-flash-off.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-flash-on.png b/assetstudio/src/images/clipart/big/10-device-access-flash-on.png Binary files differdeleted file mode 100644 index 1109aa0..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-flash-on.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-location-found.png b/assetstudio/src/images/clipart/big/10-device-access-location-found.png Binary files differdeleted file mode 100644 index d829a3c..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-location-found.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-location-off.png b/assetstudio/src/images/clipart/big/10-device-access-location-off.png Binary files differdeleted file mode 100644 index e58c258..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-location-off.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-location-searching.png b/assetstudio/src/images/clipart/big/10-device-access-location-searching.png Binary files differdeleted file mode 100644 index 3de2f26..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-location-searching.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-mic-muted.png b/assetstudio/src/images/clipart/big/10-device-access-mic-muted.png Binary files differdeleted file mode 100644 index 65b4ae6..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-mic-muted.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-mic.png b/assetstudio/src/images/clipart/big/10-device-access-mic.png Binary files differdeleted file mode 100644 index 02c1ee8..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-mic.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-network-cell.png b/assetstudio/src/images/clipart/big/10-device-access-network-cell.png Binary files differdeleted file mode 100644 index 9d60dbd..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-network-cell.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-network-wifi.png b/assetstudio/src/images/clipart/big/10-device-access-network-wifi.png Binary files differdeleted file mode 100644 index 577abdb..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-network-wifi.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-new-account.png b/assetstudio/src/images/clipart/big/10-device-access-new-account.png Binary files differdeleted file mode 100644 index d9707d8..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-new-account.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-not-secure.png b/assetstudio/src/images/clipart/big/10-device-access-not-secure.png Binary files differdeleted file mode 100644 index 2ea293a..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-not-secure.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-ring-volume.png b/assetstudio/src/images/clipart/big/10-device-access-ring-volume.png Binary files differdeleted file mode 100644 index 9d19f89..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-ring-volume.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-screen-locked-to-landscape.png b/assetstudio/src/images/clipart/big/10-device-access-screen-locked-to-landscape.png Binary files differdeleted file mode 100644 index c702480..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-screen-locked-to-landscape.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-screen-locked-to-portrait.png b/assetstudio/src/images/clipart/big/10-device-access-screen-locked-to-portrait.png Binary files differdeleted file mode 100644 index f66923c..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-screen-locked-to-portrait.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-screen-rotation.png b/assetstudio/src/images/clipart/big/10-device-access-screen-rotation.png Binary files differdeleted file mode 100644 index 22e0fcb..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-screen-rotation.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-sd-storage.png b/assetstudio/src/images/clipart/big/10-device-access-sd-storage.png Binary files differdeleted file mode 100644 index cbde363..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-sd-storage.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-secure.png b/assetstudio/src/images/clipart/big/10-device-access-secure.png Binary files differdeleted file mode 100644 index 83f4f7d..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-secure.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-storage.png b/assetstudio/src/images/clipart/big/10-device-access-storage.png Binary files differdeleted file mode 100644 index 5addbad..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-storage.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-switch-camera.png b/assetstudio/src/images/clipart/big/10-device-access-switch-camera.png Binary files differdeleted file mode 100644 index 8b2a2e3..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-switch-camera.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-switch-video.png b/assetstudio/src/images/clipart/big/10-device-access-switch-video.png Binary files differdeleted file mode 100644 index a2919f1..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-switch-video.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-time.png b/assetstudio/src/images/clipart/big/10-device-access-time.png Binary files differdeleted file mode 100644 index aa21482..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-time.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-usb.png b/assetstudio/src/images/clipart/big/10-device-access-usb.png Binary files differdeleted file mode 100644 index ba01983..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-usb.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-video.png b/assetstudio/src/images/clipart/big/10-device-access-video.png Binary files differdeleted file mode 100644 index e18c6bd..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-video.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-volume-muted.png b/assetstudio/src/images/clipart/big/10-device-access-volume-muted.png Binary files differdeleted file mode 100644 index 10433a7..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-volume-muted.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/10-device-access-volume-on.png b/assetstudio/src/images/clipart/big/10-device-access-volume-on.png Binary files differdeleted file mode 100644 index bc86a7b..0000000 --- a/assetstudio/src/images/clipart/big/10-device-access-volume-on.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/11-alerts-and-states-airplane-mode-off.png b/assetstudio/src/images/clipart/big/11-alerts-and-states-airplane-mode-off.png Binary files differdeleted file mode 100644 index f7db5e8..0000000 --- a/assetstudio/src/images/clipart/big/11-alerts-and-states-airplane-mode-off.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/11-alerts-and-states-airplane-mode-on.png b/assetstudio/src/images/clipart/big/11-alerts-and-states-airplane-mode-on.png Binary files differdeleted file mode 100644 index 7e8bf73..0000000 --- a/assetstudio/src/images/clipart/big/11-alerts-and-states-airplane-mode-on.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/11-alerts-and-states-error.png b/assetstudio/src/images/clipart/big/11-alerts-and-states-error.png Binary files differdeleted file mode 100644 index 24335f9..0000000 --- a/assetstudio/src/images/clipart/big/11-alerts-and-states-error.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/11-alerts-and-states-warning.png b/assetstudio/src/images/clipart/big/11-alerts-and-states-warning.png Binary files differdeleted file mode 100644 index be321f4..0000000 --- a/assetstudio/src/images/clipart/big/11-alerts-and-states-warning.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/12-hardware-computer.png b/assetstudio/src/images/clipart/big/12-hardware-computer.png Binary files differdeleted file mode 100644 index 6170018..0000000 --- a/assetstudio/src/images/clipart/big/12-hardware-computer.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/12-hardware-dock.png b/assetstudio/src/images/clipart/big/12-hardware-dock.png Binary files differdeleted file mode 100644 index c2fc8c8..0000000 --- a/assetstudio/src/images/clipart/big/12-hardware-dock.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/12-hardware-gamepad.png b/assetstudio/src/images/clipart/big/12-hardware-gamepad.png Binary files differdeleted file mode 100644 index 3ddc322..0000000 --- a/assetstudio/src/images/clipart/big/12-hardware-gamepad.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/12-hardware-headphones.png b/assetstudio/src/images/clipart/big/12-hardware-headphones.png Binary files differdeleted file mode 100644 index e7bce69..0000000 --- a/assetstudio/src/images/clipart/big/12-hardware-headphones.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/12-hardware-headset.png b/assetstudio/src/images/clipart/big/12-hardware-headset.png Binary files differdeleted file mode 100644 index 29f659b..0000000 --- a/assetstudio/src/images/clipart/big/12-hardware-headset.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/12-hardware-keyboard.png b/assetstudio/src/images/clipart/big/12-hardware-keyboard.png Binary files differdeleted file mode 100644 index 4a2bf70..0000000 --- a/assetstudio/src/images/clipart/big/12-hardware-keyboard.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/12-hardware-mouse.png b/assetstudio/src/images/clipart/big/12-hardware-mouse.png Binary files differdeleted file mode 100644 index 2bf8c05..0000000 --- a/assetstudio/src/images/clipart/big/12-hardware-mouse.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/12-hardware-phone.png b/assetstudio/src/images/clipart/big/12-hardware-phone.png Binary files differdeleted file mode 100644 index 423fe65..0000000 --- a/assetstudio/src/images/clipart/big/12-hardware-phone.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/2-action-about.png b/assetstudio/src/images/clipart/big/2-action-about.png Binary files differdeleted file mode 100644 index 6d43316..0000000 --- a/assetstudio/src/images/clipart/big/2-action-about.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/2-action-help.png b/assetstudio/src/images/clipart/big/2-action-help.png Binary files differdeleted file mode 100644 index 16eb8ef..0000000 --- a/assetstudio/src/images/clipart/big/2-action-help.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/2-action-search.png b/assetstudio/src/images/clipart/big/2-action-search.png Binary files differdeleted file mode 100644 index 9345a06..0000000 --- a/assetstudio/src/images/clipart/big/2-action-search.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/2-action-settings.png b/assetstudio/src/images/clipart/big/2-action-settings.png Binary files differdeleted file mode 100644 index a049ca0..0000000 --- a/assetstudio/src/images/clipart/big/2-action-settings.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/3-rating-bad.png b/assetstudio/src/images/clipart/big/3-rating-bad.png Binary files differdeleted file mode 100644 index 1ab8c5b..0000000 --- a/assetstudio/src/images/clipart/big/3-rating-bad.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/3-rating-favorite.png b/assetstudio/src/images/clipart/big/3-rating-favorite.png Binary files differdeleted file mode 100644 index 9b68720..0000000 --- a/assetstudio/src/images/clipart/big/3-rating-favorite.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/3-rating-good.png b/assetstudio/src/images/clipart/big/3-rating-good.png Binary files differdeleted file mode 100644 index c72826b..0000000 --- a/assetstudio/src/images/clipart/big/3-rating-good.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/3-rating-half-important.png b/assetstudio/src/images/clipart/big/3-rating-half-important.png Binary files differdeleted file mode 100644 index 2110a0f..0000000 --- a/assetstudio/src/images/clipart/big/3-rating-half-important.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/3-rating-important.png b/assetstudio/src/images/clipart/big/3-rating-important.png Binary files differdeleted file mode 100644 index dbad544..0000000 --- a/assetstudio/src/images/clipart/big/3-rating-important.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/3-rating-not-important.png b/assetstudio/src/images/clipart/big/3-rating-not-important.png Binary files differdeleted file mode 100644 index f7cf26f..0000000 --- a/assetstudio/src/images/clipart/big/3-rating-not-important.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/4-collections-cloud.png b/assetstudio/src/images/clipart/big/4-collections-cloud.png Binary files differdeleted file mode 100644 index a2cedbf..0000000 --- a/assetstudio/src/images/clipart/big/4-collections-cloud.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/4-collections-collection.png b/assetstudio/src/images/clipart/big/4-collections-collection.png Binary files differdeleted file mode 100644 index dfb2508..0000000 --- a/assetstudio/src/images/clipart/big/4-collections-collection.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/4-collections-go-to-today.png b/assetstudio/src/images/clipart/big/4-collections-go-to-today.png Binary files differdeleted file mode 100644 index b4971ca..0000000 --- a/assetstudio/src/images/clipart/big/4-collections-go-to-today.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/4-collections-labels.png b/assetstudio/src/images/clipart/big/4-collections-labels.png Binary files differdeleted file mode 100644 index 16f35a8..0000000 --- a/assetstudio/src/images/clipart/big/4-collections-labels.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/4-collections-new-label.png b/assetstudio/src/images/clipart/big/4-collections-new-label.png Binary files differdeleted file mode 100644 index cbf02af..0000000 --- a/assetstudio/src/images/clipart/big/4-collections-new-label.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/4-collections-sort-by-size.png b/assetstudio/src/images/clipart/big/4-collections-sort-by-size.png Binary files differdeleted file mode 100644 index 10aec0d..0000000 --- a/assetstudio/src/images/clipart/big/4-collections-sort-by-size.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/4-collections-view-as-grid.png b/assetstudio/src/images/clipart/big/4-collections-view-as-grid.png Binary files differdeleted file mode 100644 index 10a8fe3..0000000 --- a/assetstudio/src/images/clipart/big/4-collections-view-as-grid.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/4-collections-view-as-list.png b/assetstudio/src/images/clipart/big/4-collections-view-as-list.png Binary files differdeleted file mode 100644 index 5cf08e4..0000000 --- a/assetstudio/src/images/clipart/big/4-collections-view-as-list.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-attachment.png b/assetstudio/src/images/clipart/big/5-content-attachment.png Binary files differdeleted file mode 100644 index 92e6726..0000000 --- a/assetstudio/src/images/clipart/big/5-content-attachment.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-backspace.png b/assetstudio/src/images/clipart/big/5-content-backspace.png Binary files differdeleted file mode 100644 index 9a7e456..0000000 --- a/assetstudio/src/images/clipart/big/5-content-backspace.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-copy.png b/assetstudio/src/images/clipart/big/5-content-copy.png Binary files differdeleted file mode 100644 index 284a5ce..0000000 --- a/assetstudio/src/images/clipart/big/5-content-copy.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-cut.png b/assetstudio/src/images/clipart/big/5-content-cut.png Binary files differdeleted file mode 100644 index 18d1763..0000000 --- a/assetstudio/src/images/clipart/big/5-content-cut.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-discard.png b/assetstudio/src/images/clipart/big/5-content-discard.png Binary files differdeleted file mode 100644 index e40e1fe..0000000 --- a/assetstudio/src/images/clipart/big/5-content-discard.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-edit.png b/assetstudio/src/images/clipart/big/5-content-edit.png Binary files differdeleted file mode 100644 index f75157c..0000000 --- a/assetstudio/src/images/clipart/big/5-content-edit.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-email.png b/assetstudio/src/images/clipart/big/5-content-email.png Binary files differdeleted file mode 100644 index 6bec626..0000000 --- a/assetstudio/src/images/clipart/big/5-content-email.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-event.png b/assetstudio/src/images/clipart/big/5-content-event.png Binary files differdeleted file mode 100644 index dc4ed94..0000000 --- a/assetstudio/src/images/clipart/big/5-content-event.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-import-export.png b/assetstudio/src/images/clipart/big/5-content-import-export.png Binary files differdeleted file mode 100644 index 7dcd6b0..0000000 --- a/assetstudio/src/images/clipart/big/5-content-import-export.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-merge.png b/assetstudio/src/images/clipart/big/5-content-merge.png Binary files differdeleted file mode 100644 index 45ca498..0000000 --- a/assetstudio/src/images/clipart/big/5-content-merge.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-new-attachment.png b/assetstudio/src/images/clipart/big/5-content-new-attachment.png Binary files differdeleted file mode 100644 index 3e441d8..0000000 --- a/assetstudio/src/images/clipart/big/5-content-new-attachment.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-new-email.png b/assetstudio/src/images/clipart/big/5-content-new-email.png Binary files differdeleted file mode 100644 index fdcd64b..0000000 --- a/assetstudio/src/images/clipart/big/5-content-new-email.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-new-event.png b/assetstudio/src/images/clipart/big/5-content-new-event.png Binary files differdeleted file mode 100644 index 29ef513..0000000 --- a/assetstudio/src/images/clipart/big/5-content-new-event.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-new-picture.png b/assetstudio/src/images/clipart/big/5-content-new-picture.png Binary files differdeleted file mode 100644 index 1975219..0000000 --- a/assetstudio/src/images/clipart/big/5-content-new-picture.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-new.png b/assetstudio/src/images/clipart/big/5-content-new.png Binary files differdeleted file mode 100644 index 9bb4337..0000000 --- a/assetstudio/src/images/clipart/big/5-content-new.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-paste.png b/assetstudio/src/images/clipart/big/5-content-paste.png Binary files differdeleted file mode 100644 index f9393c0..0000000 --- a/assetstudio/src/images/clipart/big/5-content-paste.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-picture.png b/assetstudio/src/images/clipart/big/5-content-picture.png Binary files differdeleted file mode 100644 index dc3251b..0000000 --- a/assetstudio/src/images/clipart/big/5-content-picture.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-read.png b/assetstudio/src/images/clipart/big/5-content-read.png Binary files differdeleted file mode 100644 index 0a48d75..0000000 --- a/assetstudio/src/images/clipart/big/5-content-read.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-remove.png b/assetstudio/src/images/clipart/big/5-content-remove.png Binary files differdeleted file mode 100644 index d968d34..0000000 --- a/assetstudio/src/images/clipart/big/5-content-remove.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-save.png b/assetstudio/src/images/clipart/big/5-content-save.png Binary files differdeleted file mode 100644 index befe49a..0000000 --- a/assetstudio/src/images/clipart/big/5-content-save.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-select-all.png b/assetstudio/src/images/clipart/big/5-content-select-all.png Binary files differdeleted file mode 100644 index 572b2b5..0000000 --- a/assetstudio/src/images/clipart/big/5-content-select-all.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-split.png b/assetstudio/src/images/clipart/big/5-content-split.png Binary files differdeleted file mode 100644 index 7e5d059..0000000 --- a/assetstudio/src/images/clipart/big/5-content-split.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-undo.png b/assetstudio/src/images/clipart/big/5-content-undo.png Binary files differdeleted file mode 100644 index 07fc7d8..0000000 --- a/assetstudio/src/images/clipart/big/5-content-undo.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/5-content-unread.png b/assetstudio/src/images/clipart/big/5-content-unread.png Binary files differdeleted file mode 100644 index 41ba9e2..0000000 --- a/assetstudio/src/images/clipart/big/5-content-unread.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-add-group.png b/assetstudio/src/images/clipart/big/6-social-add-group.png Binary files differdeleted file mode 100644 index 7822f4f..0000000 --- a/assetstudio/src/images/clipart/big/6-social-add-group.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-add-person.png b/assetstudio/src/images/clipart/big/6-social-add-person.png Binary files differdeleted file mode 100644 index b335788..0000000 --- a/assetstudio/src/images/clipart/big/6-social-add-person.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-cc-bcc.png b/assetstudio/src/images/clipart/big/6-social-cc-bcc.png Binary files differdeleted file mode 100644 index 4db30a7..0000000 --- a/assetstudio/src/images/clipart/big/6-social-cc-bcc.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-chat.png b/assetstudio/src/images/clipart/big/6-social-chat.png Binary files differdeleted file mode 100644 index b0cccb3..0000000 --- a/assetstudio/src/images/clipart/big/6-social-chat.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-forward.png b/assetstudio/src/images/clipart/big/6-social-forward.png Binary files differdeleted file mode 100644 index a5abbfc..0000000 --- a/assetstudio/src/images/clipart/big/6-social-forward.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-group.png b/assetstudio/src/images/clipart/big/6-social-group.png Binary files differdeleted file mode 100644 index 1b18678..0000000 --- a/assetstudio/src/images/clipart/big/6-social-group.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-person.png b/assetstudio/src/images/clipart/big/6-social-person.png Binary files differdeleted file mode 100644 index 27ade22..0000000 --- a/assetstudio/src/images/clipart/big/6-social-person.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-reply-all.png b/assetstudio/src/images/clipart/big/6-social-reply-all.png Binary files differdeleted file mode 100644 index c2a87c6..0000000 --- a/assetstudio/src/images/clipart/big/6-social-reply-all.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-reply.png b/assetstudio/src/images/clipart/big/6-social-reply.png Binary files differdeleted file mode 100644 index 550aa80..0000000 --- a/assetstudio/src/images/clipart/big/6-social-reply.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-send-now.png b/assetstudio/src/images/clipart/big/6-social-send-now.png Binary files differdeleted file mode 100644 index c3dad3c..0000000 --- a/assetstudio/src/images/clipart/big/6-social-send-now.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/6-social-share.png b/assetstudio/src/images/clipart/big/6-social-share.png Binary files differdeleted file mode 100644 index b664970..0000000 --- a/assetstudio/src/images/clipart/big/6-social-share.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/7-location-directions.png b/assetstudio/src/images/clipart/big/7-location-directions.png Binary files differdeleted file mode 100644 index c0e67e4..0000000 --- a/assetstudio/src/images/clipart/big/7-location-directions.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/7-location-map.png b/assetstudio/src/images/clipart/big/7-location-map.png Binary files differdeleted file mode 100644 index e32dc26..0000000 --- a/assetstudio/src/images/clipart/big/7-location-map.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/7-location-place.png b/assetstudio/src/images/clipart/big/7-location-place.png Binary files differdeleted file mode 100644 index fec173c..0000000 --- a/assetstudio/src/images/clipart/big/7-location-place.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/7-location-web-site.png b/assetstudio/src/images/clipart/big/7-location-web-site.png Binary files differdeleted file mode 100644 index 4ef24a3..0000000 --- a/assetstudio/src/images/clipart/big/7-location-web-site.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/8-images-crop.png b/assetstudio/src/images/clipart/big/8-images-crop.png Binary files differdeleted file mode 100644 index bd44bf9..0000000 --- a/assetstudio/src/images/clipart/big/8-images-crop.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/8-images-rotate-left.png b/assetstudio/src/images/clipart/big/8-images-rotate-left.png Binary files differdeleted file mode 100644 index 0410adb..0000000 --- a/assetstudio/src/images/clipart/big/8-images-rotate-left.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/8-images-rotate-right.png b/assetstudio/src/images/clipart/big/8-images-rotate-right.png Binary files differdeleted file mode 100644 index abcff9c..0000000 --- a/assetstudio/src/images/clipart/big/8-images-rotate-right.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/8-images-slideshow.png b/assetstudio/src/images/clipart/big/8-images-slideshow.png Binary files differdeleted file mode 100644 index 94e47b4..0000000 --- a/assetstudio/src/images/clipart/big/8-images-slideshow.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-add-to-queue.png b/assetstudio/src/images/clipart/big/9-av-add-to-queue.png Binary files differdeleted file mode 100644 index 57b2e61..0000000 --- a/assetstudio/src/images/clipart/big/9-av-add-to-queue.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-download.png b/assetstudio/src/images/clipart/big/9-av-download.png Binary files differdeleted file mode 100644 index 46a1919..0000000 --- a/assetstudio/src/images/clipart/big/9-av-download.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-fast-forward.png b/assetstudio/src/images/clipart/big/9-av-fast-forward.png Binary files differdeleted file mode 100644 index f820f5a..0000000 --- a/assetstudio/src/images/clipart/big/9-av-fast-forward.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-full-screen.png b/assetstudio/src/images/clipart/big/9-av-full-screen.png Binary files differdeleted file mode 100644 index 1dfd01a..0000000 --- a/assetstudio/src/images/clipart/big/9-av-full-screen.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-make-available-offline.png b/assetstudio/src/images/clipart/big/9-av-make-available-offline.png Binary files differdeleted file mode 100644 index 2efcb11..0000000 --- a/assetstudio/src/images/clipart/big/9-av-make-available-offline.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-next.png b/assetstudio/src/images/clipart/big/9-av-next.png Binary files differdeleted file mode 100644 index 871587c..0000000 --- a/assetstudio/src/images/clipart/big/9-av-next.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-pause-over-video.png b/assetstudio/src/images/clipart/big/9-av-pause-over-video.png Binary files differdeleted file mode 100644 index a2665f2..0000000 --- a/assetstudio/src/images/clipart/big/9-av-pause-over-video.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-pause.png b/assetstudio/src/images/clipart/big/9-av-pause.png Binary files differdeleted file mode 100644 index 506b1d4..0000000 --- a/assetstudio/src/images/clipart/big/9-av-pause.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-play-over-video.png b/assetstudio/src/images/clipart/big/9-av-play-over-video.png Binary files differdeleted file mode 100644 index 3db3a1a..0000000 --- a/assetstudio/src/images/clipart/big/9-av-play-over-video.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-play.png b/assetstudio/src/images/clipart/big/9-av-play.png Binary files differdeleted file mode 100644 index 0c12f86..0000000 --- a/assetstudio/src/images/clipart/big/9-av-play.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-previous.png b/assetstudio/src/images/clipart/big/9-av-previous.png Binary files differdeleted file mode 100644 index 28f2596..0000000 --- a/assetstudio/src/images/clipart/big/9-av-previous.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-repeat.png b/assetstudio/src/images/clipart/big/9-av-repeat.png Binary files differdeleted file mode 100644 index 9a7a79a..0000000 --- a/assetstudio/src/images/clipart/big/9-av-repeat.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-replay.png b/assetstudio/src/images/clipart/big/9-av-replay.png Binary files differdeleted file mode 100644 index ce9df7f..0000000 --- a/assetstudio/src/images/clipart/big/9-av-replay.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-return-from-full-screen.png b/assetstudio/src/images/clipart/big/9-av-return-from-full-screen.png Binary files differdeleted file mode 100644 index 24725c0..0000000 --- a/assetstudio/src/images/clipart/big/9-av-return-from-full-screen.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-rewind.png b/assetstudio/src/images/clipart/big/9-av-rewind.png Binary files differdeleted file mode 100644 index b09f61a..0000000 --- a/assetstudio/src/images/clipart/big/9-av-rewind.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-shuffle.png b/assetstudio/src/images/clipart/big/9-av-shuffle.png Binary files differdeleted file mode 100644 index 6e90f7c..0000000 --- a/assetstudio/src/images/clipart/big/9-av-shuffle.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-stop.png b/assetstudio/src/images/clipart/big/9-av-stop.png Binary files differdeleted file mode 100644 index 9ba88ee..0000000 --- a/assetstudio/src/images/clipart/big/9-av-stop.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/9-av-upload.png b/assetstudio/src/images/clipart/big/9-av-upload.png Binary files differdeleted file mode 100644 index 41da601..0000000 --- a/assetstudio/src/images/clipart/big/9-av-upload.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/big/android.png b/assetstudio/src/images/clipart/big/android.png Binary files differdeleted file mode 100644 index d966fae..0000000 --- a/assetstudio/src/images/clipart/big/android.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/1-navigation-accept.png b/assetstudio/src/images/clipart/small/1-navigation-accept.png Binary files differdeleted file mode 100644 index f5069d9..0000000 --- a/assetstudio/src/images/clipart/small/1-navigation-accept.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/1-navigation-back.png b/assetstudio/src/images/clipart/small/1-navigation-back.png Binary files differdeleted file mode 100644 index f35aec5..0000000 --- a/assetstudio/src/images/clipart/small/1-navigation-back.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/1-navigation-cancel.png b/assetstudio/src/images/clipart/small/1-navigation-cancel.png Binary files differdeleted file mode 100644 index 4302320..0000000 --- a/assetstudio/src/images/clipart/small/1-navigation-cancel.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/1-navigation-collapse.png b/assetstudio/src/images/clipart/small/1-navigation-collapse.png Binary files differdeleted file mode 100644 index 9c40e2c..0000000 --- a/assetstudio/src/images/clipart/small/1-navigation-collapse.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/1-navigation-expand.png b/assetstudio/src/images/clipart/small/1-navigation-expand.png Binary files differdeleted file mode 100644 index 684fc5a..0000000 --- a/assetstudio/src/images/clipart/small/1-navigation-expand.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/1-navigation-forward.png b/assetstudio/src/images/clipart/small/1-navigation-forward.png Binary files differdeleted file mode 100644 index beb6cf7..0000000 --- a/assetstudio/src/images/clipart/small/1-navigation-forward.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/1-navigation-next-item.png b/assetstudio/src/images/clipart/small/1-navigation-next-item.png Binary files differdeleted file mode 100644 index 932d787..0000000 --- a/assetstudio/src/images/clipart/small/1-navigation-next-item.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/1-navigation-previous-item.png b/assetstudio/src/images/clipart/small/1-navigation-previous-item.png Binary files differdeleted file mode 100644 index 679b586..0000000 --- a/assetstudio/src/images/clipart/small/1-navigation-previous-item.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/1-navigation-refresh.png b/assetstudio/src/images/clipart/small/1-navigation-refresh.png Binary files differdeleted file mode 100644 index b946402..0000000 --- a/assetstudio/src/images/clipart/small/1-navigation-refresh.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-accounts.png b/assetstudio/src/images/clipart/small/10-device-access-accounts.png Binary files differdeleted file mode 100644 index 34b4d6a..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-accounts.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-add-alarm.png b/assetstudio/src/images/clipart/small/10-device-access-add-alarm.png Binary files differdeleted file mode 100644 index 27c528a..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-add-alarm.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-alarms.png b/assetstudio/src/images/clipart/small/10-device-access-alarms.png Binary files differdeleted file mode 100644 index 545a8fa..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-alarms.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-battery.png b/assetstudio/src/images/clipart/small/10-device-access-battery.png Binary files differdeleted file mode 100644 index 52e08bf..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-battery.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-bightness-low.png b/assetstudio/src/images/clipart/small/10-device-access-bightness-low.png Binary files differdeleted file mode 100644 index a34cdea..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-bightness-low.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-bluetooth-connected.png b/assetstudio/src/images/clipart/small/10-device-access-bluetooth-connected.png Binary files differdeleted file mode 100644 index d04e9f4..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-bluetooth-connected.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-bluetooth-searching.png b/assetstudio/src/images/clipart/small/10-device-access-bluetooth-searching.png Binary files differdeleted file mode 100644 index 06d69ae..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-bluetooth-searching.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-bluetooth.png b/assetstudio/src/images/clipart/small/10-device-access-bluetooth.png Binary files differdeleted file mode 100644 index 11ad6b3..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-bluetooth.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-brightness-auto.png b/assetstudio/src/images/clipart/small/10-device-access-brightness-auto.png Binary files differdeleted file mode 100644 index cd50b9d..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-brightness-auto.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-brightness-high.png b/assetstudio/src/images/clipart/small/10-device-access-brightness-high.png Binary files differdeleted file mode 100644 index b9d8501..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-brightness-high.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-brightness-medium.png b/assetstudio/src/images/clipart/small/10-device-access-brightness-medium.png Binary files differdeleted file mode 100644 index 7145eee..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-brightness-medium.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-call.png b/assetstudio/src/images/clipart/small/10-device-access-call.png Binary files differdeleted file mode 100644 index 732e551..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-call.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-camera.png b/assetstudio/src/images/clipart/small/10-device-access-camera.png Binary files differdeleted file mode 100644 index f61ab27..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-camera.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-data-usage.png b/assetstudio/src/images/clipart/small/10-device-access-data-usage.png Binary files differdeleted file mode 100644 index a78127f..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-data-usage.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-dial-pad.png b/assetstudio/src/images/clipart/small/10-device-access-dial-pad.png Binary files differdeleted file mode 100644 index cfbee88..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-dial-pad.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-end-call.png b/assetstudio/src/images/clipart/small/10-device-access-end-call.png Binary files differdeleted file mode 100644 index 2562d0d..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-end-call.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-flash-automatic.png b/assetstudio/src/images/clipart/small/10-device-access-flash-automatic.png Binary files differdeleted file mode 100644 index 574219c..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-flash-automatic.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-flash-off.png b/assetstudio/src/images/clipart/small/10-device-access-flash-off.png Binary files differdeleted file mode 100644 index 80e3d06..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-flash-off.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-flash-on.png b/assetstudio/src/images/clipart/small/10-device-access-flash-on.png Binary files differdeleted file mode 100644 index c9c2fff..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-flash-on.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-location-found.png b/assetstudio/src/images/clipart/small/10-device-access-location-found.png Binary files differdeleted file mode 100644 index 4221d83..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-location-found.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-location-off.png b/assetstudio/src/images/clipart/small/10-device-access-location-off.png Binary files differdeleted file mode 100644 index ea0511d..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-location-off.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-location-searching.png b/assetstudio/src/images/clipart/small/10-device-access-location-searching.png Binary files differdeleted file mode 100644 index ef9dc2e..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-location-searching.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-mic-muted.png b/assetstudio/src/images/clipart/small/10-device-access-mic-muted.png Binary files differdeleted file mode 100644 index 87bc6b3..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-mic-muted.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-mic.png b/assetstudio/src/images/clipart/small/10-device-access-mic.png Binary files differdeleted file mode 100644 index 7569d6a..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-mic.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-network-cell.png b/assetstudio/src/images/clipart/small/10-device-access-network-cell.png Binary files differdeleted file mode 100644 index 7a2c443..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-network-cell.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-network-wifi.png b/assetstudio/src/images/clipart/small/10-device-access-network-wifi.png Binary files differdeleted file mode 100644 index e25cc64..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-network-wifi.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-new-account.png b/assetstudio/src/images/clipart/small/10-device-access-new-account.png Binary files differdeleted file mode 100644 index c537899..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-new-account.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-not-secure.png b/assetstudio/src/images/clipart/small/10-device-access-not-secure.png Binary files differdeleted file mode 100644 index 89c732e..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-not-secure.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-ring-volume.png b/assetstudio/src/images/clipart/small/10-device-access-ring-volume.png Binary files differdeleted file mode 100644 index 5bfe27a..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-ring-volume.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-screen-locked-to-landscape.png b/assetstudio/src/images/clipart/small/10-device-access-screen-locked-to-landscape.png Binary files differdeleted file mode 100644 index a3b2bbb..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-screen-locked-to-landscape.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-screen-locked-to-portrait.png b/assetstudio/src/images/clipart/small/10-device-access-screen-locked-to-portrait.png Binary files differdeleted file mode 100644 index 270c069..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-screen-locked-to-portrait.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-screen-rotation.png b/assetstudio/src/images/clipart/small/10-device-access-screen-rotation.png Binary files differdeleted file mode 100644 index a5337e9..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-screen-rotation.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-sd-storage.png b/assetstudio/src/images/clipart/small/10-device-access-sd-storage.png Binary files differdeleted file mode 100644 index fe09aca..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-sd-storage.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-secure.png b/assetstudio/src/images/clipart/small/10-device-access-secure.png Binary files differdeleted file mode 100644 index 9bf3627..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-secure.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-storage.png b/assetstudio/src/images/clipart/small/10-device-access-storage.png Binary files differdeleted file mode 100644 index 1d38109..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-storage.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-switch-camera.png b/assetstudio/src/images/clipart/small/10-device-access-switch-camera.png Binary files differdeleted file mode 100644 index 972e3b3..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-switch-camera.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-switch-video.png b/assetstudio/src/images/clipart/small/10-device-access-switch-video.png Binary files differdeleted file mode 100644 index 2ae54f4..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-switch-video.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-time.png b/assetstudio/src/images/clipart/small/10-device-access-time.png Binary files differdeleted file mode 100644 index f3d932e..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-time.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-usb.png b/assetstudio/src/images/clipart/small/10-device-access-usb.png Binary files differdeleted file mode 100644 index 490d286..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-usb.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-video.png b/assetstudio/src/images/clipart/small/10-device-access-video.png Binary files differdeleted file mode 100644 index d069de4..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-video.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-volume-muted.png b/assetstudio/src/images/clipart/small/10-device-access-volume-muted.png Binary files differdeleted file mode 100644 index 283d621..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-volume-muted.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/10-device-access-volume-on.png b/assetstudio/src/images/clipart/small/10-device-access-volume-on.png Binary files differdeleted file mode 100644 index a1d6670..0000000 --- a/assetstudio/src/images/clipart/small/10-device-access-volume-on.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/11-alerts-and-states-airplane-mode-off.png b/assetstudio/src/images/clipart/small/11-alerts-and-states-airplane-mode-off.png Binary files differdeleted file mode 100644 index bfce2ee..0000000 --- a/assetstudio/src/images/clipart/small/11-alerts-and-states-airplane-mode-off.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/11-alerts-and-states-airplane-mode-on.png b/assetstudio/src/images/clipart/small/11-alerts-and-states-airplane-mode-on.png Binary files differdeleted file mode 100644 index fba67ae..0000000 --- a/assetstudio/src/images/clipart/small/11-alerts-and-states-airplane-mode-on.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/11-alerts-and-states-error.png b/assetstudio/src/images/clipart/small/11-alerts-and-states-error.png Binary files differdeleted file mode 100644 index a32766b..0000000 --- a/assetstudio/src/images/clipart/small/11-alerts-and-states-error.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/11-alerts-and-states-warning.png b/assetstudio/src/images/clipart/small/11-alerts-and-states-warning.png Binary files differdeleted file mode 100644 index 37af134..0000000 --- a/assetstudio/src/images/clipart/small/11-alerts-and-states-warning.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/12-hardware-computer.png b/assetstudio/src/images/clipart/small/12-hardware-computer.png Binary files differdeleted file mode 100644 index 91c7cdf..0000000 --- a/assetstudio/src/images/clipart/small/12-hardware-computer.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/12-hardware-dock.png b/assetstudio/src/images/clipart/small/12-hardware-dock.png Binary files differdeleted file mode 100644 index c4a20ae..0000000 --- a/assetstudio/src/images/clipart/small/12-hardware-dock.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/12-hardware-gamepad.png b/assetstudio/src/images/clipart/small/12-hardware-gamepad.png Binary files differdeleted file mode 100644 index db62572..0000000 --- a/assetstudio/src/images/clipart/small/12-hardware-gamepad.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/12-hardware-headphones.png b/assetstudio/src/images/clipart/small/12-hardware-headphones.png Binary files differdeleted file mode 100644 index 9d3b020..0000000 --- a/assetstudio/src/images/clipart/small/12-hardware-headphones.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/12-hardware-headset.png b/assetstudio/src/images/clipart/small/12-hardware-headset.png Binary files differdeleted file mode 100644 index d4efdf3..0000000 --- a/assetstudio/src/images/clipart/small/12-hardware-headset.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/12-hardware-keyboard.png b/assetstudio/src/images/clipart/small/12-hardware-keyboard.png Binary files differdeleted file mode 100644 index 7b143d2..0000000 --- a/assetstudio/src/images/clipart/small/12-hardware-keyboard.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/12-hardware-mouse.png b/assetstudio/src/images/clipart/small/12-hardware-mouse.png Binary files differdeleted file mode 100644 index 4f8d2df..0000000 --- a/assetstudio/src/images/clipart/small/12-hardware-mouse.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/12-hardware-phone.png b/assetstudio/src/images/clipart/small/12-hardware-phone.png Binary files differdeleted file mode 100644 index b5f78e1..0000000 --- a/assetstudio/src/images/clipart/small/12-hardware-phone.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/2-action-about.png b/assetstudio/src/images/clipart/small/2-action-about.png Binary files differdeleted file mode 100644 index 56a3a55..0000000 --- a/assetstudio/src/images/clipart/small/2-action-about.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/2-action-help.png b/assetstudio/src/images/clipart/small/2-action-help.png Binary files differdeleted file mode 100644 index 9104862..0000000 --- a/assetstudio/src/images/clipart/small/2-action-help.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/2-action-search.png b/assetstudio/src/images/clipart/small/2-action-search.png Binary files differdeleted file mode 100644 index 1d3f206..0000000 --- a/assetstudio/src/images/clipart/small/2-action-search.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/2-action-settings.png b/assetstudio/src/images/clipart/small/2-action-settings.png Binary files differdeleted file mode 100644 index 1dd6bbb..0000000 --- a/assetstudio/src/images/clipart/small/2-action-settings.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/3-rating-bad.png b/assetstudio/src/images/clipart/small/3-rating-bad.png Binary files differdeleted file mode 100644 index 76060f7..0000000 --- a/assetstudio/src/images/clipart/small/3-rating-bad.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/3-rating-favorite.png b/assetstudio/src/images/clipart/small/3-rating-favorite.png Binary files differdeleted file mode 100644 index b6ab63f..0000000 --- a/assetstudio/src/images/clipart/small/3-rating-favorite.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/3-rating-good.png b/assetstudio/src/images/clipart/small/3-rating-good.png Binary files differdeleted file mode 100644 index 0e6f861..0000000 --- a/assetstudio/src/images/clipart/small/3-rating-good.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/3-rating-half-important.png b/assetstudio/src/images/clipart/small/3-rating-half-important.png Binary files differdeleted file mode 100644 index d9aa154..0000000 --- a/assetstudio/src/images/clipart/small/3-rating-half-important.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/3-rating-important.png b/assetstudio/src/images/clipart/small/3-rating-important.png Binary files differdeleted file mode 100644 index 0bc5c54..0000000 --- a/assetstudio/src/images/clipart/small/3-rating-important.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/3-rating-not-important.png b/assetstudio/src/images/clipart/small/3-rating-not-important.png Binary files differdeleted file mode 100644 index 6ea5892..0000000 --- a/assetstudio/src/images/clipart/small/3-rating-not-important.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/4-collections-cloud.png b/assetstudio/src/images/clipart/small/4-collections-cloud.png Binary files differdeleted file mode 100644 index 5d80291..0000000 --- a/assetstudio/src/images/clipart/small/4-collections-cloud.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/4-collections-collection.png b/assetstudio/src/images/clipart/small/4-collections-collection.png Binary files differdeleted file mode 100644 index d4a7dcb..0000000 --- a/assetstudio/src/images/clipart/small/4-collections-collection.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/4-collections-go-to-today.png b/assetstudio/src/images/clipart/small/4-collections-go-to-today.png Binary files differdeleted file mode 100644 index 3326ead..0000000 --- a/assetstudio/src/images/clipart/small/4-collections-go-to-today.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/4-collections-labels.png b/assetstudio/src/images/clipart/small/4-collections-labels.png Binary files differdeleted file mode 100644 index e647488..0000000 --- a/assetstudio/src/images/clipart/small/4-collections-labels.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/4-collections-new-label.png b/assetstudio/src/images/clipart/small/4-collections-new-label.png Binary files differdeleted file mode 100644 index f822806..0000000 --- a/assetstudio/src/images/clipart/small/4-collections-new-label.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/4-collections-sort-by-size.png b/assetstudio/src/images/clipart/small/4-collections-sort-by-size.png Binary files differdeleted file mode 100644 index b097f67..0000000 --- a/assetstudio/src/images/clipart/small/4-collections-sort-by-size.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/4-collections-view-as-grid.png b/assetstudio/src/images/clipart/small/4-collections-view-as-grid.png Binary files differdeleted file mode 100644 index 9f9e0c1..0000000 --- a/assetstudio/src/images/clipart/small/4-collections-view-as-grid.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/4-collections-view-as-list.png b/assetstudio/src/images/clipart/small/4-collections-view-as-list.png Binary files differdeleted file mode 100644 index 39a2f1f..0000000 --- a/assetstudio/src/images/clipart/small/4-collections-view-as-list.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-attachment.png b/assetstudio/src/images/clipart/small/5-content-attachment.png Binary files differdeleted file mode 100644 index ae5dac4..0000000 --- a/assetstudio/src/images/clipart/small/5-content-attachment.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-backspace.png b/assetstudio/src/images/clipart/small/5-content-backspace.png Binary files differdeleted file mode 100644 index f2743fe..0000000 --- a/assetstudio/src/images/clipart/small/5-content-backspace.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-copy.png b/assetstudio/src/images/clipart/small/5-content-copy.png Binary files differdeleted file mode 100644 index 7efa0ec..0000000 --- a/assetstudio/src/images/clipart/small/5-content-copy.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-cut.png b/assetstudio/src/images/clipart/small/5-content-cut.png Binary files differdeleted file mode 100644 index 4f113d6..0000000 --- a/assetstudio/src/images/clipart/small/5-content-cut.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-discard.png b/assetstudio/src/images/clipart/small/5-content-discard.png Binary files differdeleted file mode 100644 index 9bbe70c..0000000 --- a/assetstudio/src/images/clipart/small/5-content-discard.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-edit.png b/assetstudio/src/images/clipart/small/5-content-edit.png Binary files differdeleted file mode 100644 index dfef46d..0000000 --- a/assetstudio/src/images/clipart/small/5-content-edit.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-email.png b/assetstudio/src/images/clipart/small/5-content-email.png Binary files differdeleted file mode 100644 index 0698571..0000000 --- a/assetstudio/src/images/clipart/small/5-content-email.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-event.png b/assetstudio/src/images/clipart/small/5-content-event.png Binary files differdeleted file mode 100644 index 4fea671..0000000 --- a/assetstudio/src/images/clipart/small/5-content-event.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-import-export.png b/assetstudio/src/images/clipart/small/5-content-import-export.png Binary files differdeleted file mode 100644 index 1b4ed11..0000000 --- a/assetstudio/src/images/clipart/small/5-content-import-export.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-merge.png b/assetstudio/src/images/clipart/small/5-content-merge.png Binary files differdeleted file mode 100644 index 4f7451e..0000000 --- a/assetstudio/src/images/clipart/small/5-content-merge.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-new-attachment.png b/assetstudio/src/images/clipart/small/5-content-new-attachment.png Binary files differdeleted file mode 100644 index 8028ea7..0000000 --- a/assetstudio/src/images/clipart/small/5-content-new-attachment.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-new-email.png b/assetstudio/src/images/clipart/small/5-content-new-email.png Binary files differdeleted file mode 100644 index 699dca9..0000000 --- a/assetstudio/src/images/clipart/small/5-content-new-email.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-new-event.png b/assetstudio/src/images/clipart/small/5-content-new-event.png Binary files differdeleted file mode 100644 index 4c4f674..0000000 --- a/assetstudio/src/images/clipart/small/5-content-new-event.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-new-picture.png b/assetstudio/src/images/clipart/small/5-content-new-picture.png Binary files differdeleted file mode 100644 index 6b7b7ea..0000000 --- a/assetstudio/src/images/clipart/small/5-content-new-picture.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-new.png b/assetstudio/src/images/clipart/small/5-content-new.png Binary files differdeleted file mode 100644 index 7ccce5b..0000000 --- a/assetstudio/src/images/clipart/small/5-content-new.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-paste.png b/assetstudio/src/images/clipart/small/5-content-paste.png Binary files differdeleted file mode 100644 index 9c3d906..0000000 --- a/assetstudio/src/images/clipart/small/5-content-paste.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-picture.png b/assetstudio/src/images/clipart/small/5-content-picture.png Binary files differdeleted file mode 100644 index 0676181..0000000 --- a/assetstudio/src/images/clipart/small/5-content-picture.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-read.png b/assetstudio/src/images/clipart/small/5-content-read.png Binary files differdeleted file mode 100644 index 7c7186f..0000000 --- a/assetstudio/src/images/clipart/small/5-content-read.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-remove.png b/assetstudio/src/images/clipart/small/5-content-remove.png Binary files differdeleted file mode 100644 index 97f11f7..0000000 --- a/assetstudio/src/images/clipart/small/5-content-remove.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-save.png b/assetstudio/src/images/clipart/small/5-content-save.png Binary files differdeleted file mode 100644 index 4b38e6c..0000000 --- a/assetstudio/src/images/clipart/small/5-content-save.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-select-all.png b/assetstudio/src/images/clipart/small/5-content-select-all.png Binary files differdeleted file mode 100644 index cfb2282..0000000 --- a/assetstudio/src/images/clipart/small/5-content-select-all.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-split.png b/assetstudio/src/images/clipart/small/5-content-split.png Binary files differdeleted file mode 100644 index 779f650..0000000 --- a/assetstudio/src/images/clipart/small/5-content-split.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-undo.png b/assetstudio/src/images/clipart/small/5-content-undo.png Binary files differdeleted file mode 100644 index 87b0129..0000000 --- a/assetstudio/src/images/clipart/small/5-content-undo.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/5-content-unread.png b/assetstudio/src/images/clipart/small/5-content-unread.png Binary files differdeleted file mode 100644 index 69cb276..0000000 --- a/assetstudio/src/images/clipart/small/5-content-unread.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-add-group.png b/assetstudio/src/images/clipart/small/6-social-add-group.png Binary files differdeleted file mode 100644 index cae89bb..0000000 --- a/assetstudio/src/images/clipart/small/6-social-add-group.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-add-person.png b/assetstudio/src/images/clipart/small/6-social-add-person.png Binary files differdeleted file mode 100644 index ec95691..0000000 --- a/assetstudio/src/images/clipart/small/6-social-add-person.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-cc-bcc.png b/assetstudio/src/images/clipart/small/6-social-cc-bcc.png Binary files differdeleted file mode 100644 index 92772f7..0000000 --- a/assetstudio/src/images/clipart/small/6-social-cc-bcc.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-chat.png b/assetstudio/src/images/clipart/small/6-social-chat.png Binary files differdeleted file mode 100644 index 675c7e3..0000000 --- a/assetstudio/src/images/clipart/small/6-social-chat.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-forward.png b/assetstudio/src/images/clipart/small/6-social-forward.png Binary files differdeleted file mode 100644 index f533b34..0000000 --- a/assetstudio/src/images/clipart/small/6-social-forward.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-group.png b/assetstudio/src/images/clipart/small/6-social-group.png Binary files differdeleted file mode 100644 index ee027a7..0000000 --- a/assetstudio/src/images/clipart/small/6-social-group.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-person.png b/assetstudio/src/images/clipart/small/6-social-person.png Binary files differdeleted file mode 100644 index bb685c7..0000000 --- a/assetstudio/src/images/clipart/small/6-social-person.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-reply-all.png b/assetstudio/src/images/clipart/small/6-social-reply-all.png Binary files differdeleted file mode 100644 index fc94679..0000000 --- a/assetstudio/src/images/clipart/small/6-social-reply-all.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-reply.png b/assetstudio/src/images/clipart/small/6-social-reply.png Binary files differdeleted file mode 100644 index e413c9a..0000000 --- a/assetstudio/src/images/clipart/small/6-social-reply.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-send-now.png b/assetstudio/src/images/clipart/small/6-social-send-now.png Binary files differdeleted file mode 100644 index 6fa79f0..0000000 --- a/assetstudio/src/images/clipart/small/6-social-send-now.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/6-social-share.png b/assetstudio/src/images/clipart/small/6-social-share.png Binary files differdeleted file mode 100644 index fb74121..0000000 --- a/assetstudio/src/images/clipart/small/6-social-share.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/7-location-directions.png b/assetstudio/src/images/clipart/small/7-location-directions.png Binary files differdeleted file mode 100644 index c3e821d..0000000 --- a/assetstudio/src/images/clipart/small/7-location-directions.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/7-location-map.png b/assetstudio/src/images/clipart/small/7-location-map.png Binary files differdeleted file mode 100644 index 4893657..0000000 --- a/assetstudio/src/images/clipart/small/7-location-map.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/7-location-place.png b/assetstudio/src/images/clipart/small/7-location-place.png Binary files differdeleted file mode 100644 index 2d41b57..0000000 --- a/assetstudio/src/images/clipart/small/7-location-place.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/7-location-web-site.png b/assetstudio/src/images/clipart/small/7-location-web-site.png Binary files differdeleted file mode 100644 index fe15c10..0000000 --- a/assetstudio/src/images/clipart/small/7-location-web-site.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/8-images-crop.png b/assetstudio/src/images/clipart/small/8-images-crop.png Binary files differdeleted file mode 100644 index ddca47f..0000000 --- a/assetstudio/src/images/clipart/small/8-images-crop.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/8-images-rotate-left.png b/assetstudio/src/images/clipart/small/8-images-rotate-left.png Binary files differdeleted file mode 100644 index 0450f2b..0000000 --- a/assetstudio/src/images/clipart/small/8-images-rotate-left.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/8-images-rotate-right.png b/assetstudio/src/images/clipart/small/8-images-rotate-right.png Binary files differdeleted file mode 100644 index a34d957..0000000 --- a/assetstudio/src/images/clipart/small/8-images-rotate-right.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/8-images-slideshow.png b/assetstudio/src/images/clipart/small/8-images-slideshow.png Binary files differdeleted file mode 100644 index 5317cf7..0000000 --- a/assetstudio/src/images/clipart/small/8-images-slideshow.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-add-to-queue.png b/assetstudio/src/images/clipart/small/9-av-add-to-queue.png Binary files differdeleted file mode 100644 index 544b8fc..0000000 --- a/assetstudio/src/images/clipart/small/9-av-add-to-queue.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-download.png b/assetstudio/src/images/clipart/small/9-av-download.png Binary files differdeleted file mode 100644 index bbf910c..0000000 --- a/assetstudio/src/images/clipart/small/9-av-download.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-fast-forward.png b/assetstudio/src/images/clipart/small/9-av-fast-forward.png Binary files differdeleted file mode 100644 index dc7e11f..0000000 --- a/assetstudio/src/images/clipart/small/9-av-fast-forward.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-full-screen.png b/assetstudio/src/images/clipart/small/9-av-full-screen.png Binary files differdeleted file mode 100644 index c1dd576..0000000 --- a/assetstudio/src/images/clipart/small/9-av-full-screen.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-make-available-offline.png b/assetstudio/src/images/clipart/small/9-av-make-available-offline.png Binary files differdeleted file mode 100644 index 8e9459c..0000000 --- a/assetstudio/src/images/clipart/small/9-av-make-available-offline.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-next.png b/assetstudio/src/images/clipart/small/9-av-next.png Binary files differdeleted file mode 100644 index 01e6543..0000000 --- a/assetstudio/src/images/clipart/small/9-av-next.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-pause-over-video.png b/assetstudio/src/images/clipart/small/9-av-pause-over-video.png Binary files differdeleted file mode 100644 index bac9ce4..0000000 --- a/assetstudio/src/images/clipart/small/9-av-pause-over-video.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-pause.png b/assetstudio/src/images/clipart/small/9-av-pause.png Binary files differdeleted file mode 100644 index 6a17d65..0000000 --- a/assetstudio/src/images/clipart/small/9-av-pause.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-play-over-video.png b/assetstudio/src/images/clipart/small/9-av-play-over-video.png Binary files differdeleted file mode 100644 index a3a68fc..0000000 --- a/assetstudio/src/images/clipart/small/9-av-play-over-video.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-play.png b/assetstudio/src/images/clipart/small/9-av-play.png Binary files differdeleted file mode 100644 index 2092eca..0000000 --- a/assetstudio/src/images/clipart/small/9-av-play.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-previous.png b/assetstudio/src/images/clipart/small/9-av-previous.png Binary files differdeleted file mode 100644 index cf10fbf..0000000 --- a/assetstudio/src/images/clipart/small/9-av-previous.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-repeat.png b/assetstudio/src/images/clipart/small/9-av-repeat.png Binary files differdeleted file mode 100644 index 7638bea..0000000 --- a/assetstudio/src/images/clipart/small/9-av-repeat.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-replay.png b/assetstudio/src/images/clipart/small/9-av-replay.png Binary files differdeleted file mode 100644 index 8f1dae0..0000000 --- a/assetstudio/src/images/clipart/small/9-av-replay.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-return-from-full-screen.png b/assetstudio/src/images/clipart/small/9-av-return-from-full-screen.png Binary files differdeleted file mode 100644 index 96949cb..0000000 --- a/assetstudio/src/images/clipart/small/9-av-return-from-full-screen.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-rewind.png b/assetstudio/src/images/clipart/small/9-av-rewind.png Binary files differdeleted file mode 100644 index 1811cd9..0000000 --- a/assetstudio/src/images/clipart/small/9-av-rewind.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-shuffle.png b/assetstudio/src/images/clipart/small/9-av-shuffle.png Binary files differdeleted file mode 100644 index 6075afb..0000000 --- a/assetstudio/src/images/clipart/small/9-av-shuffle.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-stop.png b/assetstudio/src/images/clipart/small/9-av-stop.png Binary files differdeleted file mode 100644 index 3c95c99..0000000 --- a/assetstudio/src/images/clipart/small/9-av-stop.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/9-av-upload.png b/assetstudio/src/images/clipart/small/9-av-upload.png Binary files differdeleted file mode 100644 index af9b895..0000000 --- a/assetstudio/src/images/clipart/small/9-av-upload.png +++ /dev/null diff --git a/assetstudio/src/images/clipart/small/android.png b/assetstudio/src/images/clipart/small/android.png Binary files differdeleted file mode 100644 index b2c6cd2..0000000 --- a/assetstudio/src/images/clipart/small/android.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/hdpi/back.png b/assetstudio/src/images/launcher_stencil/circle/hdpi/back.png Binary files differdeleted file mode 100644 index 5401258..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/hdpi/back.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/hdpi/fore1.png b/assetstudio/src/images/launcher_stencil/circle/hdpi/fore1.png Binary files differdeleted file mode 100644 index 6295083..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/hdpi/fore1.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/hdpi/mask.png b/assetstudio/src/images/launcher_stencil/circle/hdpi/mask.png Binary files differdeleted file mode 100644 index 7d10568..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/hdpi/mask.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/hdpi/mask_inner.png b/assetstudio/src/images/launcher_stencil/circle/hdpi/mask_inner.png Binary files differdeleted file mode 100644 index 3cbd8de..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/hdpi/mask_inner.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/mdpi/back.png b/assetstudio/src/images/launcher_stencil/circle/mdpi/back.png Binary files differdeleted file mode 100644 index 73a640f..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/mdpi/back.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/mdpi/fore1.png b/assetstudio/src/images/launcher_stencil/circle/mdpi/fore1.png Binary files differdeleted file mode 100644 index 90a9e47..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/mdpi/fore1.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/mdpi/mask.png b/assetstudio/src/images/launcher_stencil/circle/mdpi/mask.png Binary files differdeleted file mode 100644 index 107082b..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/mdpi/mask.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/mdpi/mask_inner.png b/assetstudio/src/images/launcher_stencil/circle/mdpi/mask_inner.png Binary files differdeleted file mode 100644 index c44b167..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/mdpi/mask_inner.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/web/back.png b/assetstudio/src/images/launcher_stencil/circle/web/back.png Binary files differdeleted file mode 100644 index 2058604..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/web/back.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/web/fore1.png b/assetstudio/src/images/launcher_stencil/circle/web/fore1.png Binary files differdeleted file mode 100644 index b80d804..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/web/fore1.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/web/mask.png b/assetstudio/src/images/launcher_stencil/circle/web/mask.png Binary files differdeleted file mode 100644 index 709060e..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/web/mask.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/web/mask_inner.png b/assetstudio/src/images/launcher_stencil/circle/web/mask_inner.png Binary files differdeleted file mode 100644 index dca886c..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/web/mask_inner.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/xhdpi/back.png b/assetstudio/src/images/launcher_stencil/circle/xhdpi/back.png Binary files differdeleted file mode 100644 index 4fd8b7d..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/xhdpi/back.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/xhdpi/fore1.png b/assetstudio/src/images/launcher_stencil/circle/xhdpi/fore1.png Binary files differdeleted file mode 100644 index c0d752b..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/xhdpi/fore1.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/xhdpi/mask.png b/assetstudio/src/images/launcher_stencil/circle/xhdpi/mask.png Binary files differdeleted file mode 100644 index a0bfc98..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/xhdpi/mask.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/xhdpi/mask_inner.png b/assetstudio/src/images/launcher_stencil/circle/xhdpi/mask_inner.png Binary files differdeleted file mode 100644 index 02bc806..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/xhdpi/mask_inner.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/xxhdpi/back.png b/assetstudio/src/images/launcher_stencil/circle/xxhdpi/back.png Binary files differdeleted file mode 100644 index 8b62d02..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/xxhdpi/back.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/xxhdpi/fore1.png b/assetstudio/src/images/launcher_stencil/circle/xxhdpi/fore1.png Binary files differdeleted file mode 100644 index 2e4ab8c..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/xxhdpi/fore1.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/xxhdpi/mask.png b/assetstudio/src/images/launcher_stencil/circle/xxhdpi/mask.png Binary files differdeleted file mode 100644 index 874aa5b..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/xxhdpi/mask.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/circle/xxhdpi/mask_inner.png b/assetstudio/src/images/launcher_stencil/circle/xxhdpi/mask_inner.png Binary files differdeleted file mode 100644 index 819c144..0000000 --- a/assetstudio/src/images/launcher_stencil/circle/xxhdpi/mask_inner.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/hdpi/back.png b/assetstudio/src/images/launcher_stencil/square/hdpi/back.png Binary files differdeleted file mode 100644 index 284a785..0000000 --- a/assetstudio/src/images/launcher_stencil/square/hdpi/back.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/hdpi/fore1.png b/assetstudio/src/images/launcher_stencil/square/hdpi/fore1.png Binary files differdeleted file mode 100644 index e7faa71..0000000 --- a/assetstudio/src/images/launcher_stencil/square/hdpi/fore1.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/hdpi/mask.png b/assetstudio/src/images/launcher_stencil/square/hdpi/mask.png Binary files differdeleted file mode 100644 index fe1aa7a..0000000 --- a/assetstudio/src/images/launcher_stencil/square/hdpi/mask.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/hdpi/mask_inner.png b/assetstudio/src/images/launcher_stencil/square/hdpi/mask_inner.png Binary files differdeleted file mode 100644 index 28df306..0000000 --- a/assetstudio/src/images/launcher_stencil/square/hdpi/mask_inner.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/mdpi/back.png b/assetstudio/src/images/launcher_stencil/square/mdpi/back.png Binary files differdeleted file mode 100644 index 849a656..0000000 --- a/assetstudio/src/images/launcher_stencil/square/mdpi/back.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/mdpi/fore1.png b/assetstudio/src/images/launcher_stencil/square/mdpi/fore1.png Binary files differdeleted file mode 100644 index cef3e48..0000000 --- a/assetstudio/src/images/launcher_stencil/square/mdpi/fore1.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/mdpi/mask.png b/assetstudio/src/images/launcher_stencil/square/mdpi/mask.png Binary files differdeleted file mode 100644 index 8b3d0a2..0000000 --- a/assetstudio/src/images/launcher_stencil/square/mdpi/mask.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/mdpi/mask_inner.png b/assetstudio/src/images/launcher_stencil/square/mdpi/mask_inner.png Binary files differdeleted file mode 100644 index 3f3ad9b..0000000 --- a/assetstudio/src/images/launcher_stencil/square/mdpi/mask_inner.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/web/back.png b/assetstudio/src/images/launcher_stencil/square/web/back.png Binary files differdeleted file mode 100644 index bb59979..0000000 --- a/assetstudio/src/images/launcher_stencil/square/web/back.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/web/fore1.png b/assetstudio/src/images/launcher_stencil/square/web/fore1.png Binary files differdeleted file mode 100644 index 2a3be1c..0000000 --- a/assetstudio/src/images/launcher_stencil/square/web/fore1.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/web/mask.png b/assetstudio/src/images/launcher_stencil/square/web/mask.png Binary files differdeleted file mode 100644 index e3f10a3..0000000 --- a/assetstudio/src/images/launcher_stencil/square/web/mask.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/web/mask_inner.png b/assetstudio/src/images/launcher_stencil/square/web/mask_inner.png Binary files differdeleted file mode 100644 index ea15b03..0000000 --- a/assetstudio/src/images/launcher_stencil/square/web/mask_inner.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/xhdpi/back.png b/assetstudio/src/images/launcher_stencil/square/xhdpi/back.png Binary files differdeleted file mode 100644 index 1338f7c..0000000 --- a/assetstudio/src/images/launcher_stencil/square/xhdpi/back.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/xhdpi/fore1.png b/assetstudio/src/images/launcher_stencil/square/xhdpi/fore1.png Binary files differdeleted file mode 100644 index 90c3430..0000000 --- a/assetstudio/src/images/launcher_stencil/square/xhdpi/fore1.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/xhdpi/mask.png b/assetstudio/src/images/launcher_stencil/square/xhdpi/mask.png Binary files differdeleted file mode 100644 index 7291db1..0000000 --- a/assetstudio/src/images/launcher_stencil/square/xhdpi/mask.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/xhdpi/mask_inner.png b/assetstudio/src/images/launcher_stencil/square/xhdpi/mask_inner.png Binary files differdeleted file mode 100644 index 388a4c2..0000000 --- a/assetstudio/src/images/launcher_stencil/square/xhdpi/mask_inner.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/xxhdpi/back.png b/assetstudio/src/images/launcher_stencil/square/xxhdpi/back.png Binary files differdeleted file mode 100644 index 068e28f..0000000 --- a/assetstudio/src/images/launcher_stencil/square/xxhdpi/back.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/xxhdpi/fore1.png b/assetstudio/src/images/launcher_stencil/square/xxhdpi/fore1.png Binary files differdeleted file mode 100644 index 099b135..0000000 --- a/assetstudio/src/images/launcher_stencil/square/xxhdpi/fore1.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/xxhdpi/mask.png b/assetstudio/src/images/launcher_stencil/square/xxhdpi/mask.png Binary files differdeleted file mode 100644 index be26125..0000000 --- a/assetstudio/src/images/launcher_stencil/square/xxhdpi/mask.png +++ /dev/null diff --git a/assetstudio/src/images/launcher_stencil/square/xxhdpi/mask_inner.png b/assetstudio/src/images/launcher_stencil/square/xxhdpi/mask_inner.png Binary files differdeleted file mode 100644 index 3f8c5ea..0000000 --- a/assetstudio/src/images/launcher_stencil/square/xxhdpi/mask_inner.png +++ /dev/null diff --git a/assetstudio/src/images/notification_stencil/hdpi.png b/assetstudio/src/images/notification_stencil/hdpi.png Binary files differdeleted file mode 100644 index f755f4f..0000000 --- a/assetstudio/src/images/notification_stencil/hdpi.png +++ /dev/null diff --git a/assetstudio/src/images/notification_stencil/mdpi.png b/assetstudio/src/images/notification_stencil/mdpi.png Binary files differdeleted file mode 100644 index 5f80247..0000000 --- a/assetstudio/src/images/notification_stencil/mdpi.png +++ /dev/null diff --git a/assetstudio/src/images/notification_stencil/xhdpi.png b/assetstudio/src/images/notification_stencil/xhdpi.png Binary files differdeleted file mode 100644 index 7c27d82..0000000 --- a/assetstudio/src/images/notification_stencil/xhdpi.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/ActionBarIconGeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/ActionBarIconGeneratorTest.java deleted file mode 100644 index a097f0a..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/ActionBarIconGeneratorTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.assetstudiolib; - -import com.android.assetstudiolib.ActionBarIconGenerator.ActionBarOptions; -import com.android.assetstudiolib.ActionBarIconGenerator.Theme; - -import java.io.IOException; - -@SuppressWarnings("javadoc") -public class ActionBarIconGeneratorTest extends GeneratorTest { - private void checkGraphic(String baseName, Theme theme) throws IOException { - ActionBarOptions options = new ActionBarOptions(); - options.theme = theme; - - ActionBarIconGenerator generator = new ActionBarIconGenerator(); - checkGraphic(3, "actions", baseName, generator, options); - } - - public void testDark() throws Exception { - checkGraphic("ic_action_dark", Theme.HOLO_DARK); - } - - public void testLight() throws Exception { - checkGraphic("ic_action_light", Theme.HOLO_LIGHT); - } -} diff --git a/assetstudio/tests/src/com/android/assetstudiolib/GeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/GeneratorTest.java deleted file mode 100644 index 4a96f30..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/GeneratorTest.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.assetstudiolib; - -import java.awt.Color; -import java.awt.Graphics; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.imageio.ImageIO; - -import junit.framework.TestCase; - -/** - * Shared test infrastructure for code generator - */ -public abstract class GeneratorTest extends TestCase implements GraphicGeneratorContext { - private static final String TEST_DATA_REL_PATH = - "assetstudio/tests/src/com/android/assetstudiolib/testdata"; - - protected void checkGraphic(int expectedFileCount, String folderName, String baseName, - GraphicGenerator generator, GraphicGenerator.Options options) - throws IOException { - Map<String, Map<String, BufferedImage>> categoryMap = - new HashMap<String, Map<String, BufferedImage>>(); - options.sourceImage = GraphicGenerator.getClipartImage("android.png"); - generator.generate(null, categoryMap, this, options, baseName); - - File targetDir = getTargetDir(); - - List<String> errors = new ArrayList<String>(); - int fileCount = 0; - for (Map<String, BufferedImage> previews : categoryMap.values()) { - for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) { - String relativePath = entry.getKey(); - BufferedImage image = entry.getValue(); - - String path = "testdata" + File.separator + folderName + File.separator - + relativePath; - InputStream is = GeneratorTest.class.getResourceAsStream(path); - if (is == null) { - if (targetDir == null) { - fail("Did not find " + path - + ". Set ADT_SDK_SOURCE_PATH to have it created automatically"); - } - File fileName = new File(targetDir, folderName + File.separator - + relativePath); - assertFalse(fileName.exists()); - if (!fileName.getParentFile().exists()) { - boolean mkdir = fileName.getParentFile().mkdirs(); - assertTrue(fileName.getParent(), mkdir); - } - - ImageIO.write(image, "PNG", fileName); - errors.add("File did not exist, created " + fileName.getPath()); - } else { - BufferedImage goldenImage = ImageIO.read(is); - assertImageSimilar(relativePath, goldenImage, image, 5.0f); - } - } - - fileCount += previews.values().size(); - } - if (errors.size() > 0) { - fail(errors.toString()); - } - - assertEquals("Wrong number of generated files", expectedFileCount, fileCount); - } - - private void assertImageSimilar(String imageName, BufferedImage goldenImage, - BufferedImage image, float maxPercentDifferent) throws IOException { - assertTrue("Widths differ too much for " + imageName, Math.abs(goldenImage.getWidth() - - image.getWidth()) < 2); - assertTrue("Widths differ too much for " + imageName, Math.abs(goldenImage.getHeight() - - image.getHeight()) < 2); - - assertEquals(BufferedImage.TYPE_INT_ARGB, image.getType()); - - if (goldenImage.getType() != BufferedImage.TYPE_INT_ARGB) { - BufferedImage temp = new BufferedImage(goldenImage.getWidth(), goldenImage.getHeight(), - BufferedImage.TYPE_INT_ARGB); - temp.getGraphics().drawImage(goldenImage, 0, 0, null); - goldenImage = temp; - } - assertEquals(BufferedImage.TYPE_INT_ARGB, goldenImage.getType()); - - int imageWidth = Math.min(goldenImage.getWidth(), image.getWidth()); - int imageHeight = Math.min(goldenImage.getHeight(), image.getHeight()); - - // Blur the images to account for the scenarios where there are pixel - // differences - // in where a sharp edge occurs - // goldenImage = blur(goldenImage, 6); - // image = blur(image, 6); - - int width = 3 * imageWidth; - int height = imageHeight; - BufferedImage deltaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - Graphics g = deltaImage.getGraphics(); - - // Compute delta map - long delta = 0; - for (int y = 0; y < imageHeight; y++) { - for (int x = 0; x < imageWidth; x++) { - int goldenRgb = goldenImage.getRGB(x, y); - int rgb = image.getRGB(x, y); - if (goldenRgb == rgb) { - deltaImage.setRGB(imageWidth + x, y, 0x00808080); - continue; - } - - // If the pixels have no opacity, don't delta colors at all - if (((goldenRgb & 0xFF000000) == 0) && (rgb & 0xFF000000) == 0) { - deltaImage.setRGB(imageWidth + x, y, 0x00808080); - continue; - } - - int deltaR = ((rgb & 0xFF0000) >>> 16) - ((goldenRgb & 0xFF0000) >>> 16); - int newR = 128 + deltaR & 0xFF; - int deltaG = ((rgb & 0x00FF00) >>> 8) - ((goldenRgb & 0x00FF00) >>> 8); - int newG = 128 + deltaG & 0xFF; - int deltaB = (rgb & 0x0000FF) - (goldenRgb & 0x0000FF); - int newB = 128 + deltaB & 0xFF; - - int avgAlpha = ((((goldenRgb & 0xFF000000) >>> 24) - + ((rgb & 0xFF000000) >>> 24)) / 2) << 24; - - int newRGB = avgAlpha | newR << 16 | newG << 8 | newB; - deltaImage.setRGB(imageWidth + x, y, newRGB); - - delta += Math.abs(deltaR); - delta += Math.abs(deltaG); - delta += Math.abs(deltaB); - } - } - - // 3 different colors, 256 color levels - long total = imageHeight * imageWidth * 3L * 256L; - float percentDifference = (float) (delta * 100 / (double) total); - - if (percentDifference > maxPercentDifferent) { - // Expected on the left - // Golden on the right - g.drawImage(goldenImage, 0, 0, null); - g.drawImage(image, 2 * imageWidth, 0, null); - - // Labels - if (imageWidth > 80) { - g.setColor(Color.RED); - g.drawString("Expected", 10, 20); - g.drawString("Actual", 2 * imageWidth + 10, 20); - } - - File output = new File(getTempDir(), "delta-" - + imageName.replace(File.separatorChar, '_')); - if (output.exists()) { - output.delete(); - } - ImageIO.write(deltaImage, "PNG", output); - String message = String.format("Images differ (by %.1f%%) - see details in %s", - percentDifference, output); - System.out.println(message); - fail(message); - } - - g.dispose(); - } - - protected File getTempDir() { - if (System.getProperty("os.name").equals("Mac OS X")) { - return new File("/tmp"); //$NON-NLS-1$ - } - - return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$ - } - - @Override - public BufferedImage loadImageResource(String path) { - try { - return GraphicGenerator.getStencilImage(path); - } catch (IOException e) { - fail(e.toString()); - } - - return null; - } - - /** Get the location to write missing golden files to */ - protected File getTargetDir() { - // Set $ADT_SDK_SOURCE_PATH to point to your git "sdk" directory - String sdk = System.getenv("ADT_SDK_SOURCE_PATH"); - if (sdk != null) { - File sdkPath = new File(sdk); - if (sdkPath.exists()) { - File testData = new File(sdkPath, TEST_DATA_REL_PATH.replace('/', - File.separatorChar)); - if (testData.exists()) { - return testData; - } - } - } - - return null; - } -} diff --git a/assetstudio/tests/src/com/android/assetstudiolib/LauncherIconGeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/LauncherIconGeneratorTest.java deleted file mode 100644 index 2ab09b1..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/LauncherIconGeneratorTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.assetstudiolib; - -import com.android.assetstudiolib.LauncherIconGenerator.LauncherOptions; - -import java.io.IOException; - -@SuppressWarnings("javadoc") -public class LauncherIconGeneratorTest extends GeneratorTest { - private void checkGraphic(String baseName, - GraphicGenerator.Shape shape, GraphicGenerator.Style style, - boolean crop, int background, boolean isWebGraphic) throws IOException { - LauncherOptions options = new LauncherOptions(); - options.shape = shape; - options.crop = crop; - options.style = style; - options.backgroundColor = background; - options.isWebGraphic = isWebGraphic; - - LauncherIconGenerator generator = new LauncherIconGenerator(); - checkGraphic(4 + (isWebGraphic ? 1 : 0), "launcher", baseName, generator, options); - } - - public void testLauncher_simpleCircle() throws Exception { - checkGraphic("red_simple_circle", GraphicGenerator.Shape.CIRCLE, - GraphicGenerator.Style.SIMPLE, true, 0xFF0000, true); - } - - // The glossy rendering type is no longer included since it doesn't match the - // style guide. - //public void testLauncher_glossySquare() throws Exception { - // checkGraphic("blue_glossy_square", GraphicGenerator.Shape.SQUARE, - // GraphicGenerator.Style.GLOSSY, true, 0x0040FF, true); - //} -} diff --git a/assetstudio/tests/src/com/android/assetstudiolib/MenuIconGeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/MenuIconGeneratorTest.java deleted file mode 100644 index 544777d..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/MenuIconGeneratorTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.assetstudiolib; - -import java.io.IOException; - -@SuppressWarnings("javadoc") -public class MenuIconGeneratorTest extends GeneratorTest { - private void checkGraphic(String baseName) throws IOException { - MenuIconGenerator generator = new MenuIconGenerator(); - checkGraphic(3, "menus", baseName, generator, new GraphicGenerator.Options()); - } - - public void testMenu() throws Exception { - checkGraphic("ic_menu_1"); - } -} diff --git a/assetstudio/tests/src/com/android/assetstudiolib/NotificationIconGeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/NotificationIconGeneratorTest.java deleted file mode 100644 index 39fd7ac..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/NotificationIconGeneratorTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.assetstudiolib; - -import com.android.assetstudiolib.NotificationIconGenerator.NotificationOptions; - -import java.io.IOException; - -@SuppressWarnings("javadoc") -public class NotificationIconGeneratorTest extends GeneratorTest { - private void checkGraphic(String baseName, int minSdk, String folderName, - int expectedCount) throws IOException { - NotificationOptions options = new NotificationOptions(); - options.minSdk = minSdk; - - NotificationIconGenerator generator = new NotificationIconGenerator(); - checkGraphic(expectedCount, folderName, baseName, generator, options); - } - - private void checkGraphic(String baseName) throws IOException { - checkGraphic(baseName, 1, "notification", 9); - } - - public void testNotification1() throws Exception { - checkGraphic("ic_stat_1"); - } - - public void testNotification2() throws Exception { - checkGraphic("ic_stat_1", 9 /* minSdk */, "notification-v9+", 6 /* fileCount */); - } - - public void testNotification3() throws Exception { - checkGraphic("ic_stat_1", 11, "notification-v11+", 3); - } -} diff --git a/assetstudio/tests/src/com/android/assetstudiolib/TabIconGeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/TabIconGeneratorTest.java deleted file mode 100644 index fb7849c..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/TabIconGeneratorTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.assetstudiolib; - -import java.io.IOException; - -@SuppressWarnings("javadoc") -public class TabIconGeneratorTest extends GeneratorTest { - private void checkGraphic(String folderName, String baseName, int minSdk, - int expectedFileCount) throws IOException { - TabIconGenerator generator = new TabIconGenerator(); - TabIconGenerator.TabOptions options = new TabIconGenerator.TabOptions(); - options.minSdk = minSdk; - checkGraphic(expectedFileCount, folderName, baseName, generator, options); - } - - public void testTabs1() throws Exception { - checkGraphic("tabs", "ic_tab_1", 1 /* minSdk */, 12 /* expectedFileCount */); - } - - public void testTabs2() throws Exception { - checkGraphic("tabs-v5+", "ic_tab_1", 5, 6); - } -} diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_dark.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_dark.png Binary files differdeleted file mode 100644 index f8950e9..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_dark.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_light.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_light.png Binary files differdeleted file mode 100644 index 0904e2f..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_light.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_dark.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_dark.png Binary files differdeleted file mode 100644 index 08a41b6..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_dark.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_light.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_light.png Binary files differdeleted file mode 100644 index 11d4db7..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_light.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_dark.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_dark.png Binary files differdeleted file mode 100644 index 7396c15..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_dark.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_light.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_light.png Binary files differdeleted file mode 100644 index 941e377..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_light.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/red_simple_circle-web.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/red_simple_circle-web.png Binary files differdeleted file mode 100644 index 7805116..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/red_simple_circle-web.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-hdpi/red_simple_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-hdpi/red_simple_circle.png Binary files differdeleted file mode 100644 index 6fdf98e..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-hdpi/red_simple_circle.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-mdpi/red_simple_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-mdpi/red_simple_circle.png Binary files differdeleted file mode 100644 index 18a677f..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-mdpi/red_simple_circle.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xhdpi/red_simple_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xhdpi/red_simple_circle.png Binary files differdeleted file mode 100644 index 85de4ee..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xhdpi/red_simple_circle.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xxhdpi/red_simple_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xxhdpi/red_simple_circle.png Binary files differdeleted file mode 100644 index 5670c8c..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xxhdpi/red_simple_circle.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-hdpi/ic_menu_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-hdpi/ic_menu_1.png Binary files differdeleted file mode 100644 index 0d6d67f..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-hdpi/ic_menu_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-mdpi/ic_menu_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-mdpi/ic_menu_1.png Binary files differdeleted file mode 100644 index de68199..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-mdpi/ic_menu_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-xhdpi/ic_menu_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-xhdpi/ic_menu_1.png Binary files differdeleted file mode 100644 index dca9a75..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-xhdpi/ic_menu_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v11+/res/drawable-hdpi/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v11+/res/drawable-hdpi/ic_stat_1.png Binary files differdeleted file mode 100644 index 4273bef..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v11+/res/drawable-hdpi/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v11+/res/drawable-mdpi/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v11+/res/drawable-mdpi/ic_stat_1.png Binary files differdeleted file mode 100644 index c8994f5..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v11+/res/drawable-mdpi/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v11+/res/drawable-xhdpi/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v11+/res/drawable-xhdpi/ic_stat_1.png Binary files differdeleted file mode 100644 index 1643eba..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v11+/res/drawable-xhdpi/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-hdpi-v11/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-hdpi-v11/ic_stat_1.png Binary files differdeleted file mode 100644 index 4273bef..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-hdpi-v11/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-hdpi/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-hdpi/ic_stat_1.png Binary files differdeleted file mode 100644 index 2fc269b..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-hdpi/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-mdpi-v11/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-mdpi-v11/ic_stat_1.png Binary files differdeleted file mode 100644 index c8994f5..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-mdpi-v11/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-mdpi/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-mdpi/ic_stat_1.png Binary files differdeleted file mode 100644 index 524e31a..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-mdpi/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-xhdpi-v11/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-xhdpi-v11/ic_stat_1.png Binary files differdeleted file mode 100644 index 1643eba..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-xhdpi-v11/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-xhdpi/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-xhdpi/ic_stat_1.png Binary files differdeleted file mode 100644 index 8b24336..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification-v9+/res/drawable-xhdpi/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v11/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v11/ic_stat_1.png Binary files differdeleted file mode 100644 index 4273bef..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v11/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v9/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v9/ic_stat_1.png Binary files differdeleted file mode 100644 index 2fc269b..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v9/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi/ic_stat_1.png Binary files differdeleted file mode 100644 index a1c9285..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v11/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v11/ic_stat_1.png Binary files differdeleted file mode 100644 index c8994f5..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v11/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v9/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v9/ic_stat_1.png Binary files differdeleted file mode 100644 index 524e31a..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v9/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi/ic_stat_1.png Binary files differdeleted file mode 100644 index 40b27af..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v11/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v11/ic_stat_1.png Binary files differdeleted file mode 100644 index 1643eba..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v11/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v9/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v9/ic_stat_1.png Binary files differdeleted file mode 100644 index 8b24336..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v9/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi/ic_stat_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi/ic_stat_1.png Binary files differdeleted file mode 100644 index c7159ec..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi/ic_stat_1.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-hdpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-hdpi/ic_tab_1_selected.png Binary files differdeleted file mode 100644 index 1e367dc..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-hdpi/ic_tab_1_selected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-hdpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-hdpi/ic_tab_1_unselected.png Binary files differdeleted file mode 100644 index 8d1ea96..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-hdpi/ic_tab_1_unselected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-ldpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-ldpi/ic_tab_1_selected.png Binary files differdeleted file mode 100644 index 1d79c30..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-ldpi/ic_tab_1_selected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-ldpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-ldpi/ic_tab_1_unselected.png Binary files differdeleted file mode 100644 index 4ed95d7..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-ldpi/ic_tab_1_unselected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-mdpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-mdpi/ic_tab_1_selected.png Binary files differdeleted file mode 100644 index c741050..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-mdpi/ic_tab_1_selected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-mdpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-mdpi/ic_tab_1_unselected.png Binary files differdeleted file mode 100644 index c11de02..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-mdpi/ic_tab_1_unselected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-xhdpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-xhdpi/ic_tab_1_selected.png Binary files differdeleted file mode 100644 index f61e1de..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-xhdpi/ic_tab_1_selected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-xhdpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-xhdpi/ic_tab_1_unselected.png Binary files differdeleted file mode 100644 index 52852ee..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs-v5+/res/drawable-xhdpi/ic_tab_1_unselected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_selected.png Binary files differdeleted file mode 100644 index 1e367dc..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_selected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_unselected.png Binary files differdeleted file mode 100644 index 8d1ea96..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_unselected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_selected.png Binary files differdeleted file mode 100644 index 5a49be9..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_selected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_unselected.png Binary files differdeleted file mode 100644 index d957240..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_unselected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_selected.png Binary files differdeleted file mode 100644 index c741050..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_selected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_unselected.png Binary files differdeleted file mode 100644 index c11de02..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_unselected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_selected.png Binary files differdeleted file mode 100644 index 7c603b7..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_selected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_unselected.png Binary files differdeleted file mode 100644 index 521bf60..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_unselected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_selected.png Binary files differdeleted file mode 100644 index f61e1de..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_selected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_unselected.png Binary files differdeleted file mode 100644 index 52852ee..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_unselected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_selected.png Binary files differdeleted file mode 100644 index 317fc5a..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_selected.png +++ /dev/null diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_unselected.png Binary files differdeleted file mode 100644 index 7a0ffc6..0000000 --- a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_unselected.png +++ /dev/null diff --git a/build/product_sdk.mk b/build/product_sdk.mk index 226dba3..cf4879e 100644 --- a/build/product_sdk.mk +++ b/build/product_sdk.mk @@ -33,13 +33,11 @@ PRODUCT_PACKAGES += \ anttasks \ archquery \ assetstudio \ - common-tests \ ddmlib \ ddms \ ddmuilib \ draw9patch \ dvlib \ - dvlib-tests \ hierarchyviewer \ sdk_common \ layoutlib_api \ diff --git a/build/tools.atree b/build/tools.atree index a8c9da6..b5a7edd 100644 --- a/build/tools.atree +++ b/build/tools.atree @@ -69,9 +69,9 @@ bin/draw9patch tools/draw9patch bin/traceview tools/traceview bin/android tools/android bin/monkeyrunner tools/monkeyrunner -bin/lint tools/lint bin/uiautomatorviewer tools/uiautomatorviewer -prebuilts/devtools/jobb/etc/jobb tools/jobb +prebuilts/devtools/etc/jobb tools/jobb +prebuilts/devtools/etc/lint tools/lint # sdk.git Ant templates for project build files sdk/templates/build.template tools/lib/build.template @@ -94,36 +94,36 @@ external/qemu/android/avd/hardware-properties.ini tools/lib/hardware-properties. sdk/files/android.el tools/lib/android.el # Java Libraries for the tools -framework/common.jar tools/lib/common.jar -framework/swtmenubar.jar tools/lib/swtmenubar.jar -sdk/apkbuilder/etc/apkbuilder tools/apkbuilder -framework/sdkstats.jar tools/lib/sdkstats.jar -framework/archquery.jar tools/lib/archquery.jar -framework/ddms.jar tools/lib/ddms.jar -framework/ddmlib.jar tools/lib/ddmlib.jar -framework/ddmuilib.jar tools/lib/ddmuilib.jar -framework/hierarchyviewer2.jar tools/lib/hierarchyviewer2.jar -framework/hierarchyviewerlib.jar tools/lib/hierarchyviewerlib.jar -framework/draw9patch.jar tools/lib/draw9patch.jar -framework/traceview.jar tools/lib/traceview.jar -framework/anttasks.jar tools/lib/anttasks.jar -framework/sdklib.jar tools/lib/sdklib.jar -framework/sdkuilib.jar tools/lib/sdkuilib.jar -framework/sdkmanager.jar tools/lib/sdkmanager.jar -framework/monkeyrunner.jar tools/lib/monkeyrunner.jar -framework/chimpchat.jar tools/lib/chimpchat.jar -framework/guava-tools.jar tools/lib/guava-tools.jar -framework/jsilver.jar tools/lib/jsilver.jar -framework/jython.jar tools/lib/jython.jar -framework/lint.jar tools/lib/lint.jar -framework/lint_api.jar tools/lib/lint_api.jar -framework/lint_checks.jar tools/lib/lint_checks.jar -framework/manifmerger.jar tools/lib/manifmerger.jar -framework/dvlib.jar tools/lib/dvlib.jar -framework/layoutlib_api.jar tools/lib/layoutlib_api.jar -framework/uiautomatorviewer.jar tools/lib/uiautomatorviewer.jar -framework/jobb.jar tools/lib/jobb.jar -framework/fat32lib.jar tools/lib/fat32lib.jar +prebuilts/devtools/common.jar tools/lib/common.jar +prebuilts/devtools/swtmenubar.jar tools/lib/swtmenubar.jar +sdk/apkbuilder/etc/apkbuilder tools/apkbuilder +framework/sdkstats.jar tools/lib/sdkstats.jar +framework/archquery.jar tools/lib/archquery.jar +framework/ddms.jar tools/lib/ddms.jar +prebuilts/devtools/ddmlib.jar tools/lib/ddmlib.jar +framework/ddmuilib.jar tools/lib/ddmuilib.jar +framework/hierarchyviewer2.jar tools/lib/hierarchyviewer2.jar +framework/hierarchyviewerlib.jar tools/lib/hierarchyviewerlib.jar +framework/draw9patch.jar tools/lib/draw9patch.jar +framework/traceview.jar tools/lib/traceview.jar +framework/anttasks.jar tools/lib/anttasks.jar +prebuilts/devtools/sdklib.jar tools/lib/sdklib.jar +prebuilts/devtools/sdkuilib.jar tools/lib/sdkuilib.jar +framework/sdkmanager.jar tools/lib/sdkmanager.jar +framework/monkeyrunner.jar tools/lib/monkeyrunner.jar +framework/chimpchat.jar tools/lib/chimpchat.jar +framework/guava-tools.jar tools/lib/guava-tools.jar +framework/jsilver.jar tools/lib/jsilver.jar +framework/jython.jar tools/lib/jython.jar +prebuilts/devtools/lint.jar tools/lib/lint.jar +prebuilts/devtools/lint_api.jar tools/lib/lint_api.jar +prebuilts/devtools/lint_checks.jar tools/lib/lint_checks.jar +prebuilts/devtools/manifmerger.jar tools/lib/manifmerger.jar +prebuilts/devtools/dvlib.jar tools/lib/dvlib.jar +prebuilts/devtools/layoutlib_api.jar tools/lib/layoutlib_api.jar +framework/uiautomatorviewer.jar tools/lib/uiautomatorviewer.jar +prebuilts/devtools/jobb.jar tools/lib/jobb.jar +framework/fat32lib.jar tools/lib/fat32lib.jar prebuilts/tools/common/mkidentity/mkidentity-prebuilt.jar tools/lib/mkidentity.jar # 3rd Party java libraries @@ -135,7 +135,7 @@ prebuilts/tools/common/http-client/commons-logging-1.1.1.jar prebuilts/tools/common/http-client/commons-codec-1.4.jar tools/lib/commons-codec-1.4.jar framework/emmalib.jar tools/lib/emma_device.jar external/emma/lib/emma.jar tools/lib/emma.jar -external/emma/lib/emma_ant.jar tools/lib/emma_ant.jar +external/emma/lib/emma_ant.jar tools/lib/emma_ant.jar prebuilts/tools/common/jfreechart/jcommon-1.0.12.jar tools/lib/jcommon-1.0.12.jar prebuilts/tools/common/jfreechart/jfreechart-1.0.9.jar tools/lib/jfreechart-1.0.9.jar prebuilts/tools/common/jfreechart/jfreechart-1.0.9-swt.jar tools/lib/jfreechart-1.0.9-swt.jar @@ -143,7 +143,6 @@ prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.ja prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar tools/lib/org.eclipse.equinox.common_3.6.0.v20100503.jar prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar tools/lib/org.eclipse.jface_3.6.2.M20110210-1200.jar prebuilts/tools/common/osgi/osgi.jar tools/lib/osgi.jar -prebuilts/tools/common/swing-worker/swing-worker-1.1.jar tools/lib/swing-worker-1.1.jar prebuilts/tools/common/asm-tools/asm-4.0.jar tools/lib/asm-4.0.jar prebuilts/tools/common/asm-tools/asm-tree-4.0.jar tools/lib/asm-tree-4.0.jar prebuilts/tools/common/asm-tools/asm-analysis-4.0.jar tools/lib/asm-analysis-4.0.jar @@ -191,7 +190,4 @@ sdk/files/typos tools/support sdk/testapps tests/testapps framework/ninepatch-tests.jar tests/libtests/ninepatch-tests.jar -framework/common-tests.jar tests/libtests/common-tests.jar -framework/layoutlib_api.jar tests/libtests/layoutlib_api.jar -framework/dvlib-tests.jar tests/libtests/dvlib-tests.jar diff --git a/build/tools.windows.atree b/build/tools.windows.atree index f936d96..3a84ff8 100755 --- a/build/tools.windows.atree +++ b/build/tools.windows.atree @@ -37,10 +37,10 @@ rm tools/draw9patch sdk/draw9patch/etc/draw9patch.bat tools/draw9patch.bat rm tools/lint -sdk/lint/cli/etc/lint.bat tools/lint.bat +prebuilts/devtools/etc/lint.bat tools/lint.bat rm tools/jobb -prebuilts/devtools/jobb/etc/jobb.bat tools/jobb.bat +prebuilts/devtools/etc/jobb.bat tools/jobb.bat rm tools/emulator diff --git a/chimpchat/src/com/android/chimpchat/adb/AdbChimpDevice.java b/chimpchat/src/com/android/chimpchat/adb/AdbChimpDevice.java index dadb017..af09efe 100644 --- a/chimpchat/src/com/android/chimpchat/adb/AdbChimpDevice.java +++ b/chimpchat/src/com/android/chimpchat/adb/AdbChimpDevice.java @@ -367,6 +367,9 @@ public class AdbChimpDevice implements IChimpDevice { case DOWN_AND_UP: manager.tap(x, y); break; + case MOVE: + manager.touchMove(x, y); + break; } } catch (IOException e) { LOG.log(Level.SEVERE, "Error sending touch event: " + x + " " + y + " " + type, e); diff --git a/chimpchat/src/com/android/chimpchat/core/TouchPressType.java b/chimpchat/src/com/android/chimpchat/core/TouchPressType.java index e5b92b7..7e1d4b6 100644 --- a/chimpchat/src/com/android/chimpchat/core/TouchPressType.java +++ b/chimpchat/src/com/android/chimpchat/core/TouchPressType.java @@ -23,7 +23,7 @@ import java.util.Map; * When passed as a string, the "identifier" value is used. */ public enum TouchPressType { - DOWN("down"), UP("up"), DOWN_AND_UP("downAndUp"); + DOWN("down"), UP("up"), DOWN_AND_UP("downAndUp"), MOVE("move"); private static final Map<String,TouchPressType> identifierToEnum = new HashMap<String,TouchPressType>(); diff --git a/common/Android.mk b/common/Android.mk index 85bcd60..dc098fd 100644 --- a/common/Android.mk +++ b/common/Android.mk @@ -16,13 +16,12 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java) - -LOCAL_JAR_MANIFEST := manifest.txt +# The common code has moved to tools/base/common. +# The rule below uses the prebuilt common.jar. +# +# If you want to run the tests, cd to tools/base/common +# and run ./gradlew :common:test -# IMPORTANT: if you add a new dependency here, please make sure -# to also check the following files: -# common/manifest.txt LOCAL_JAVA_LIBRARIES := \ guava-tools @@ -30,16 +29,8 @@ LOCAL_MODULE := common LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_JAVA_LIBRARY) - -# build the tests -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src/test/java) - -LOCAL_MODULE := common-tests -LOCAL_MODULE_TAGS := optional +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + ../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) -LOCAL_JAVA_LIBRARIES := common junit +include $(BUILD_HOST_PREBUILT) -include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/common/src/main/java/com/android/SdkConstants.java b/common/src/main/java/com/android/SdkConstants.java deleted file mode 100644 index a0fdc39..0000000 --- a/common/src/main/java/com/android/SdkConstants.java +++ /dev/null @@ -1,1168 +0,0 @@ -/* - * Copyright (C) 2007 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; - -import java.io.File; - -/** - * Constant definition class.<br> - * <br> - * Most constants have a prefix defining the content. - * <ul> - * <li><code>OS_</code> OS path constant. These paths are different depending on the platform.</li> - * <li><code>FN_</code> File name constant.</li> - * <li><code>FD_</code> Folder name constant.</li> - * <li><code>TAG_</code> XML element tag name</li> - * <li><code>ATTR_</code> XML attribute name</li> - * <li><code>VALUE_</code> XML attribute value</li> - * <li><code>CLASS_</code> Class name</li> - * <li><code>DOT_</code> File name extension, including the dot </li> - * <li><code>EXT_</code> File name extension, without the dot </li> - * </ul> - */ -@SuppressWarnings("javadoc") // Not documenting all the fields here -public final class SdkConstants { - public static final int PLATFORM_UNKNOWN = 0; - public static final int PLATFORM_LINUX = 1; - public static final int PLATFORM_WINDOWS = 2; - public static final int PLATFORM_DARWIN = 3; - - /** - * Returns current platform, one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, - * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. - */ - public static final int CURRENT_PLATFORM = currentPlatform(); - - /** - * Charset for the ini file handled by the SDK. - */ - public static final String INI_CHARSET = "UTF-8"; //$NON-NLS-1$ - - /** An SDK Project's AndroidManifest.xml file */ - public static final String FN_ANDROID_MANIFEST_XML= "AndroidManifest.xml"; //$NON-NLS-1$ - /** pre-dex jar filename. i.e. "classes.jar" */ - public static final String FN_CLASSES_JAR = "classes.jar"; //$NON-NLS-1$ - /** Dex filename inside the APK. i.e. "classes.dex" */ - public static final String FN_APK_CLASSES_DEX = "classes.dex"; //$NON-NLS-1$ - - /** An SDK Project's build.xml file */ - public static final String FN_BUILD_XML = "build.xml"; //$NON-NLS-1$ - - /** Name of the framework library, i.e. "android.jar" */ - public static final String FN_FRAMEWORK_LIBRARY = "android.jar"; //$NON-NLS-1$ - /** Name of the framework library, i.e. "uiautomator.jar" */ - public static final String FN_UI_AUTOMATOR_LIBRARY = "uiautomator.jar"; //$NON-NLS-1$ - /** Name of the layout attributes, i.e. "attrs.xml" */ - public static final String FN_ATTRS_XML = "attrs.xml"; //$NON-NLS-1$ - /** Name of the layout attributes, i.e. "attrs_manifest.xml" */ - public static final String FN_ATTRS_MANIFEST_XML = "attrs_manifest.xml"; //$NON-NLS-1$ - /** framework aidl import file */ - public static final String FN_FRAMEWORK_AIDL = "framework.aidl"; //$NON-NLS-1$ - /** framework renderscript folder */ - public static final String FN_FRAMEWORK_RENDERSCRIPT = "renderscript"; //$NON-NLS-1$ - /** framework include folder */ - public static final String FN_FRAMEWORK_INCLUDE = "include"; //$NON-NLS-1$ - /** framework include (clang) folder */ - public static final String FN_FRAMEWORK_INCLUDE_CLANG = "clang-include"; //$NON-NLS-1$ - /** layoutlib.jar file */ - public static final String FN_LAYOUTLIB_JAR = "layoutlib.jar"; //$NON-NLS-1$ - /** widget list file */ - public static final String FN_WIDGETS = "widgets.txt"; //$NON-NLS-1$ - /** Intent activity actions list file */ - public static final String FN_INTENT_ACTIONS_ACTIVITY = "activity_actions.txt"; //$NON-NLS-1$ - /** Intent broadcast actions list file */ - public static final String FN_INTENT_ACTIONS_BROADCAST = "broadcast_actions.txt"; //$NON-NLS-1$ - /** Intent service actions list file */ - public static final String FN_INTENT_ACTIONS_SERVICE = "service_actions.txt"; //$NON-NLS-1$ - /** Intent category list file */ - public static final String FN_INTENT_CATEGORIES = "categories.txt"; //$NON-NLS-1$ - - /** annotations support jar */ - public static final String FN_ANNOTATIONS_JAR = "annotations.jar"; //$NON-NLS-1$ - - /** platform build property file */ - public static final String FN_BUILD_PROP = "build.prop"; //$NON-NLS-1$ - /** plugin properties file */ - public static final String FN_PLUGIN_PROP = "plugin.prop"; //$NON-NLS-1$ - /** add-on manifest file */ - public static final String FN_MANIFEST_INI = "manifest.ini"; //$NON-NLS-1$ - /** add-on layout device XML file. */ - public static final String FN_DEVICES_XML = "devices.xml"; //$NON-NLS-1$ - /** hardware properties definition file */ - public static final String FN_HARDWARE_INI = "hardware-properties.ini"; //$NON-NLS-1$ - - /** project property file */ - public static final String FN_PROJECT_PROPERTIES = "project.properties"; //$NON-NLS-1$ - - /** project local property file */ - public static final String FN_LOCAL_PROPERTIES = "local.properties"; //$NON-NLS-1$ - - /** project ant property file */ - public static final String FN_ANT_PROPERTIES = "ant.properties"; //$NON-NLS-1$ - - /** Skin layout file */ - public static final String FN_SKIN_LAYOUT = "layout"; //$NON-NLS-1$ - - /** dx.jar file */ - public static final String FN_DX_JAR = "dx.jar"; //$NON-NLS-1$ - - /** dx executable (with extension for the current OS) */ - public static final String FN_DX = - "dx" + ext(".bat", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - /** aapt executable (with extension for the current OS) */ - public static final String FN_AAPT = - "aapt" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - /** aidl executable (with extension for the current OS) */ - public static final String FN_AIDL = - "aidl" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - /** renderscript executable (with extension for the current OS) */ - public static final String FN_RENDERSCRIPT = - "llvm-rs-cc" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - /** adb executable (with extension for the current OS) */ - public static final String FN_ADB = - "adb" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - /** emulator executable for the current OS */ - public static final String FN_EMULATOR = - "emulator" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - /** zipalign executable (with extension for the current OS) */ - public static final String FN_ZIPALIGN = - "zipalign" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - /** dexdump executable (with extension for the current OS) */ - public static final String FN_DEXDUMP = - "dexdump" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - /** proguard executable (with extension for the current OS) */ - public static final String FN_PROGUARD = - "proguard" + ext(".bat", ".sh"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - /** find_lock for Windows (with extension for the current OS) */ - public static final String FN_FIND_LOCK = - "find_lock" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - /** properties file for SDK Updater packages */ - public static final String FN_SOURCE_PROP = "source.properties"; //$NON-NLS-1$ - /** properties file for content hash of installed packages */ - public static final String FN_CONTENT_HASH_PROP = "content_hash.properties"; //$NON-NLS-1$ - /** properties file for the SDK */ - public static final String FN_SDK_PROP = "sdk.properties"; //$NON-NLS-1$ - - /** - * filename for gdbserver. - */ - public static final String FN_GDBSERVER = "gdbserver"; //$NON-NLS-1$ - - /** global Android proguard config file */ - public static final String FN_ANDROID_PROGUARD_FILE = "proguard-android.txt"; //$NON-NLS-1$ - /** global Android proguard config file with optimization enabled */ - public static final String FN_ANDROID_OPT_PROGUARD_FILE = "proguard-android-optimize.txt"; //$NON-NLS-1$ - /** default proguard config file with new file extension (for project specific stuff) */ - public static final String FN_PROJECT_PROGUARD_FILE = "proguard-project.txt"; //$NON-NLS-1$ - - /* Folder Names for Android Projects . */ - - /** Resources folder name, i.e. "res". */ - public static final String FD_RESOURCES = "res"; //$NON-NLS-1$ - /** Assets folder name, i.e. "assets" */ - public static final String FD_ASSETS = "assets"; //$NON-NLS-1$ - /** Default source folder name in an SDK project, i.e. "src". - * <p/> - * Note: this is not the same as {@link #FD_PKG_SOURCES} - * which is an SDK sources folder for packages. */ - public static final String FD_SOURCES = "src"; //$NON-NLS-1$ - /** Default generated source folder name, i.e. "gen" */ - public static final String FD_GEN_SOURCES = "gen"; //$NON-NLS-1$ - /** Default native library folder name inside the project, i.e. "libs" - * While the folder inside the .apk is "lib", we call that one libs because - * that's what we use in ant for both .jar and .so and we need to make the 2 development ways - * compatible. */ - public static final String FD_NATIVE_LIBS = "libs"; //$NON-NLS-1$ - /** Native lib folder inside the APK: "lib" */ - public static final String FD_APK_NATIVE_LIBS = "lib"; //$NON-NLS-1$ - /** Default output folder name, i.e. "bin" */ - public static final String FD_OUTPUT = "bin"; //$NON-NLS-1$ - /** Classes output folder name, i.e. "classes" */ - public static final String FD_CLASSES_OUTPUT = "classes"; //$NON-NLS-1$ - /** proguard output folder for mapping, etc.. files */ - public static final String FD_PROGUARD = "proguard"; //$NON-NLS-1$ - /** aidl output folder for copied aidl files */ - public static final String FD_AIDL = "aidl"; //$NON-NLS-1$ - - /* Folder Names for the Android SDK */ - - /** Name of the SDK platforms folder. */ - public static final String FD_PLATFORMS = "platforms"; //$NON-NLS-1$ - /** Name of the SDK addons folder. */ - public static final String FD_ADDONS = "add-ons"; //$NON-NLS-1$ - /** Name of the SDK system-images folder. */ - public static final String FD_SYSTEM_IMAGES = "system-images"; //$NON-NLS-1$ - /** Name of the SDK sources folder where source packages are installed. - * <p/> - * Note this is not the same as {@link #FD_SOURCES} which is the folder name where sources - * are installed inside a project. */ - public static final String FD_PKG_SOURCES = "sources"; //$NON-NLS-1$ - /** Name of the SDK tools folder. */ - public static final String FD_TOOLS = "tools"; //$NON-NLS-1$ - /** Name of the SDK tools/support folder. */ - public static final String FD_SUPPORT = "support"; //$NON-NLS-1$ - /** Name of the SDK platform tools folder. */ - public static final String FD_PLATFORM_TOOLS = "platform-tools"; //$NON-NLS-1$ - /** Name of the SDK tools/lib folder. */ - public static final String FD_LIB = "lib"; //$NON-NLS-1$ - /** Name of the SDK docs folder. */ - public static final String FD_DOCS = "docs"; //$NON-NLS-1$ - /** Name of the doc folder containing API reference doc (javadoc) */ - public static final String FD_DOCS_REFERENCE = "reference"; //$NON-NLS-1$ - /** Name of the SDK images folder. */ - public static final String FD_IMAGES = "images"; //$NON-NLS-1$ - /** Name of the ABI to support. */ - public static final String ABI_ARMEABI = "armeabi"; //$NON-NLS-1$ - public static final String ABI_ARMEABI_V7A = "armeabi-v7a"; //$NON-NLS-1$ - public static final String ABI_INTEL_ATOM = "x86"; //$NON-NLS-1$ - public static final String ABI_MIPS = "mips"; //$NON-NLS-1$ - /** Name of the CPU arch to support. */ - public static final String CPU_ARCH_ARM = "arm"; //$NON-NLS-1$ - public static final String CPU_ARCH_INTEL_ATOM = "x86"; //$NON-NLS-1$ - public static final String CPU_ARCH_MIPS = "mips"; //$NON-NLS-1$ - /** Name of the CPU model to support. */ - public static final String CPU_MODEL_CORTEX_A8 = "cortex-a8"; //$NON-NLS-1$ - - /** Name of the SDK skins folder. */ - public static final String FD_SKINS = "skins"; //$NON-NLS-1$ - /** Name of the SDK samples folder. */ - public static final String FD_SAMPLES = "samples"; //$NON-NLS-1$ - /** Name of the SDK extras folder. */ - public static final String FD_EXTRAS = "extras"; //$NON-NLS-1$ - /** - * Name of an extra's sample folder. - * Ideally extras should have one {@link #FD_SAMPLES} folder containing - * one or more sub-folders (one per sample). However some older extras - * might contain a single "sample" folder with directly the samples files - * in it. When possible we should encourage extras' owners to move to the - * multi-samples format. - */ - public static final String FD_SAMPLE = "sample"; //$NON-NLS-1$ - /** Name of the SDK templates folder, i.e. "templates" */ - public static final String FD_TEMPLATES = "templates"; //$NON-NLS-1$ - /** Name of the SDK Ant folder, i.e. "ant" */ - public static final String FD_ANT = "ant"; //$NON-NLS-1$ - /** Name of the SDK data folder, i.e. "data" */ - public static final String FD_DATA = "data"; //$NON-NLS-1$ - /** Name of the SDK renderscript folder, i.e. "rs" */ - public static final String FD_RENDERSCRIPT = "rs"; //$NON-NLS-1$ - /** Name of the SDK resources folder, i.e. "res" */ - public static final String FD_RES = "res"; //$NON-NLS-1$ - /** Name of the SDK font folder, i.e. "fonts" */ - public static final String FD_FONTS = "fonts"; //$NON-NLS-1$ - /** Name of the android sources directory */ - public static final String FD_ANDROID_SOURCES = "sources"; //$NON-NLS-1$ - /** Name of the addon libs folder. */ - public static final String FD_ADDON_LIBS = "libs"; //$NON-NLS-1$ - - /** Name of the cache folder in the $HOME/.android. */ - public static final String FD_CACHE = "cache"; //$NON-NLS-1$ - - /** API codename of a release (non preview) system image or platform. **/ - public static final String CODENAME_RELEASE = "REL"; //$NON-NLS-1$ - - /** Namespace for the resource XML, i.e. "http://schemas.android.com/apk/res/android" */ - public static final String NS_RESOURCES = - "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$ - - /** Namespace for the device schema, i.e. "http://schemas.android.com/sdk/devices/1" */ - public static final String NS_DEVICES_XSD = - "http://schemas.android.com/sdk/devices/1"; //$NON-NLS-1$ - - - /** The name of the uses-library that provides "android.test.runner" */ - public static final String ANDROID_TEST_RUNNER_LIB = - "android.test.runner"; //$NON-NLS-1$ - - /* Folder path relative to the SDK root */ - /** Path of the documentation directory relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public static final String OS_SDK_DOCS_FOLDER = FD_DOCS + File.separator; - - /** Path of the tools directory relative to the sdk folder, or to a platform folder. - * This is an OS path, ending with a separator. */ - public static final String OS_SDK_TOOLS_FOLDER = FD_TOOLS + File.separator; - - /** Path of the lib directory relative to the sdk folder, or to a platform folder. - * This is an OS path, ending with a separator. */ - public static final String OS_SDK_TOOLS_LIB_FOLDER = - OS_SDK_TOOLS_FOLDER + FD_LIB + File.separator; - - /** - * Path of the lib directory relative to the sdk folder, or to a platform - * folder. This is an OS path, ending with a separator. - */ - public static final String OS_SDK_TOOLS_LIB_EMULATOR_FOLDER = OS_SDK_TOOLS_LIB_FOLDER - + "emulator" + File.separator; //$NON-NLS-1$ - - /** Path of the platform tools directory relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public static final String OS_SDK_PLATFORM_TOOLS_FOLDER = FD_PLATFORM_TOOLS + File.separator; - - /** Path of the Platform tools Lib directory relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public static final String OS_SDK_PLATFORM_TOOLS_LIB_FOLDER = - OS_SDK_PLATFORM_TOOLS_FOLDER + FD_LIB + File.separator; - - /** Path of the bin folder of proguard folder relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public static final String OS_SDK_TOOLS_PROGUARD_BIN_FOLDER = - SdkConstants.OS_SDK_TOOLS_FOLDER + - "proguard" + File.separator + //$NON-NLS-1$ - "bin" + File.separator; //$NON-NLS-1$ - - /* Folder paths relative to a platform or add-on folder */ - - /** Path of the images directory relative to a platform or addon folder. - * This is an OS path, ending with a separator. */ - public static final String OS_IMAGES_FOLDER = FD_IMAGES + File.separator; - - /** Path of the skin directory relative to a platform or addon folder. - * This is an OS path, ending with a separator. */ - public static final String OS_SKINS_FOLDER = FD_SKINS + File.separator; - - /* Folder paths relative to a Platform folder */ - - /** Path of the data directory relative to a platform folder. - * This is an OS path, ending with a separator. */ - public static final String OS_PLATFORM_DATA_FOLDER = FD_DATA + File.separator; - - /** Path of the renderscript directory relative to a platform folder. - * This is an OS path, ending with a separator. */ - public static final String OS_PLATFORM_RENDERSCRIPT_FOLDER = FD_RENDERSCRIPT + File.separator; - - - /** Path of the samples directory relative to a platform folder. - * This is an OS path, ending with a separator. */ - public static final String OS_PLATFORM_SAMPLES_FOLDER = FD_SAMPLES + File.separator; - - /** Path of the resources directory relative to a platform folder. - * This is an OS path, ending with a separator. */ - public static final String OS_PLATFORM_RESOURCES_FOLDER = - OS_PLATFORM_DATA_FOLDER + FD_RES + File.separator; - - /** Path of the fonts directory relative to a platform folder. - * This is an OS path, ending with a separator. */ - public static final String OS_PLATFORM_FONTS_FOLDER = - OS_PLATFORM_DATA_FOLDER + FD_FONTS + File.separator; - - /** Path of the android source directory relative to a platform folder. - * This is an OS path, ending with a separator. */ - public static final String OS_PLATFORM_SOURCES_FOLDER = FD_ANDROID_SOURCES + File.separator; - - /** Path of the android templates directory relative to a platform folder. - * This is an OS path, ending with a separator. */ - public static final String OS_PLATFORM_TEMPLATES_FOLDER = FD_TEMPLATES + File.separator; - - /** Path of the Ant build rules directory relative to a platform folder. - * This is an OS path, ending with a separator. */ - public static final String OS_PLATFORM_ANT_FOLDER = FD_ANT + File.separator; - - /** Path of the attrs.xml file relative to a platform folder. */ - public static final String OS_PLATFORM_ATTRS_XML = - OS_PLATFORM_RESOURCES_FOLDER + SdkConstants.FD_RES_VALUES + File.separator + - FN_ATTRS_XML; - - /** Path of the attrs_manifest.xml file relative to a platform folder. */ - public static final String OS_PLATFORM_ATTRS_MANIFEST_XML = - OS_PLATFORM_RESOURCES_FOLDER + SdkConstants.FD_RES_VALUES + File.separator + - FN_ATTRS_MANIFEST_XML; - - /** Path of the layoutlib.jar file relative to a platform folder. */ - public static final String OS_PLATFORM_LAYOUTLIB_JAR = - OS_PLATFORM_DATA_FOLDER + FN_LAYOUTLIB_JAR; - - /** Path of the renderscript include folder relative to a platform folder. */ - public static final String OS_FRAMEWORK_RS = - FN_FRAMEWORK_RENDERSCRIPT + File.separator + FN_FRAMEWORK_INCLUDE; - /** Path of the renderscript (clang) include folder relative to a platform folder. */ - public static final String OS_FRAMEWORK_RS_CLANG = - FN_FRAMEWORK_RENDERSCRIPT + File.separator + FN_FRAMEWORK_INCLUDE_CLANG; - - /* Folder paths relative to a addon folder */ - /** Path of the images directory relative to a folder folder. - * This is an OS path, ending with a separator. */ - public static final String OS_ADDON_LIBS_FOLDER = FD_ADDON_LIBS + File.separator; - - /** Skin default **/ - public static final String SKIN_DEFAULT = "default"; //$NON-NLS-1$ - - /** SDK property: ant templates revision */ - public static final String PROP_SDK_ANT_TEMPLATES_REVISION = - "sdk.ant.templates.revision"; //$NON-NLS-1$ - - /** SDK property: default skin */ - public static final String PROP_SDK_DEFAULT_SKIN = "sdk.skin.default"; //$NON-NLS-1$ - - /* Android Class Constants */ - public static final String CLASS_ACTIVITY = "android.app.Activity"; //$NON-NLS-1$ - public static final String CLASS_APPLICATION = "android.app.Application"; //$NON-NLS-1$ - public static final String CLASS_SERVICE = "android.app.Service"; //$NON-NLS-1$ - public static final String CLASS_BROADCASTRECEIVER = "android.content.BroadcastReceiver"; //$NON-NLS-1$ - public static final String CLASS_CONTENTPROVIDER = "android.content.ContentProvider"; //$NON-NLS-1$ - public static final String CLASS_INSTRUMENTATION = "android.app.Instrumentation"; //$NON-NLS-1$ - public static final String CLASS_INSTRUMENTATION_RUNNER = - "android.test.InstrumentationTestRunner"; //$NON-NLS-1$ - public static final String CLASS_BUNDLE = "android.os.Bundle"; //$NON-NLS-1$ - public static final String CLASS_R = "android.R"; //$NON-NLS-1$ - public static final String CLASS_MANIFEST_PERMISSION = "android.Manifest$permission"; //$NON-NLS-1$ - public static final String CLASS_INTENT = "android.content.Intent"; //$NON-NLS-1$ - public static final String CLASS_CONTEXT = "android.content.Context"; //$NON-NLS-1$ - public static final String CLASS_VIEW = "android.view.View"; //$NON-NLS-1$ - public static final String CLASS_VIEWGROUP = "android.view.ViewGroup"; //$NON-NLS-1$ - public static final String CLASS_NAME_LAYOUTPARAMS = "LayoutParams"; //$NON-NLS-1$ - public static final String CLASS_VIEWGROUP_LAYOUTPARAMS = - CLASS_VIEWGROUP + "$" + CLASS_NAME_LAYOUTPARAMS; //$NON-NLS-1$ - public static final String CLASS_NAME_FRAMELAYOUT = "FrameLayout"; //$NON-NLS-1$ - public static final String CLASS_FRAMELAYOUT = - "android.widget." + CLASS_NAME_FRAMELAYOUT; //$NON-NLS-1$ - public static final String CLASS_PREFERENCE = "android.preference.Preference"; //$NON-NLS-1$ - public static final String CLASS_NAME_PREFERENCE_SCREEN = "PreferenceScreen"; //$NON-NLS-1$ - public static final String CLASS_PREFERENCES = - "android.preference." + CLASS_NAME_PREFERENCE_SCREEN; //$NON-NLS-1$ - public static final String CLASS_PREFERENCEGROUP = "android.preference.PreferenceGroup"; //$NON-NLS-1$ - public static final String CLASS_PARCELABLE = "android.os.Parcelable"; //$NON-NLS-1$ - public static final String CLASS_FRAGMENT = "android.app.Fragment"; //$NON-NLS-1$ - public static final String CLASS_V4_FRAGMENT = "android.support.v4.app.Fragment"; //$NON-NLS-1$ - /** MockView is part of the layoutlib bridge and used to display classes that have - * no rendering in the graphical layout editor. */ - public static final String CLASS_MOCK_VIEW = "com.android.layoutlib.bridge.MockView"; //$NON-NLS-1$ - - /** Returns the appropriate name for the 'android' command, which is 'android.exe' for - * Windows and 'android' for all other platforms. */ - public static String androidCmdName() { - String os = System.getProperty("os.name"); //$NON-NLS-1$ - String cmd = "android"; //$NON-NLS-1$ - if (os.startsWith("Windows")) { //$NON-NLS-1$ - cmd += ".bat"; //$NON-NLS-1$ - } - return cmd; - } - - /** Returns the appropriate name for the 'mksdcard' command, which is 'mksdcard.exe' for - * Windows and 'mkdsdcard' for all other platforms. */ - public static String mkSdCardCmdName() { - String os = System.getProperty("os.name"); //$NON-NLS-1$ - String cmd = "mksdcard"; //$NON-NLS-1$ - if (os.startsWith("Windows")) { //$NON-NLS-1$ - cmd += ".exe"; //$NON-NLS-1$ - } - return cmd; - } - - /** - * Returns current platform - * - * @return one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, - * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. - */ - public static int currentPlatform() { - String os = System.getProperty("os.name"); //$NON-NLS-1$ - if (os.startsWith("Mac OS")) { //$NON-NLS-1$ - return PLATFORM_DARWIN; - } else if (os.startsWith("Windows")) { //$NON-NLS-1$ - return PLATFORM_WINDOWS; - } else if (os.startsWith("Linux")) { //$NON-NLS-1$ - return PLATFORM_LINUX; - } - - return PLATFORM_UNKNOWN; - } - - /** - * Returns current platform's UI name - * - * @return one of "Windows", "Mac OS X", "Linux" or "other". - */ - public static String currentPlatformName() { - String os = System.getProperty("os.name"); //$NON-NLS-1$ - if (os.startsWith("Mac OS")) { //$NON-NLS-1$ - return "Mac OS X"; //$NON-NLS-1$ - } else if (os.startsWith("Windows")) { //$NON-NLS-1$ - return "Windows"; //$NON-NLS-1$ - } else if (os.startsWith("Linux")) { //$NON-NLS-1$ - return "Linux"; //$NON-NLS-1$ - } - - return "Other"; - } - - private static String ext(String windowsExtension, String nonWindowsExtension) { - if (CURRENT_PLATFORM == PLATFORM_WINDOWS) { - return windowsExtension; - } else { - return nonWindowsExtension; - } - } - - /** Default anim resource folder name, i.e. "anim" */ - public static final String FD_RES_ANIM = "anim"; //$NON-NLS-1$ - /** Default animator resource folder name, i.e. "animator" */ - public static final String FD_RES_ANIMATOR = "animator"; //$NON-NLS-1$ - /** Default color resource folder name, i.e. "color" */ - public static final String FD_RES_COLOR = "color"; //$NON-NLS-1$ - /** Default drawable resource folder name, i.e. "drawable" */ - public static final String FD_RES_DRAWABLE = "drawable"; //$NON-NLS-1$ - /** Default interpolator resource folder name, i.e. "interpolator" */ - public static final String FD_RES_INTERPOLATOR = "interpolator"; //$NON-NLS-1$ - /** Default layout resource folder name, i.e. "layout" */ - public static final String FD_RES_LAYOUT = "layout"; //$NON-NLS-1$ - /** Default menu resource folder name, i.e. "menu" */ - public static final String FD_RES_MENU = "menu"; //$NON-NLS-1$ - /** Default menu resource folder name, i.e. "mipmap" */ - public static final String FD_RES_MIPMAP = "mipmap"; //$NON-NLS-1$ - /** Default values resource folder name, i.e. "values" */ - public static final String FD_RES_VALUES = "values"; //$NON-NLS-1$ - /** Default xml resource folder name, i.e. "xml" */ - public static final String FD_RES_XML = "xml"; //$NON-NLS-1$ - /** Default raw resource folder name, i.e. "raw" */ - public static final String FD_RES_RAW = "raw"; //$NON-NLS-1$ - /** Separator between the resource folder qualifier. */ - public static final String RES_QUALIFIER_SEP = "-"; //$NON-NLS-1$ - /** Namespace used in XML files for Android attributes */ - - // ---- XML ---- - - /** URI of the reserved "xmlns" prefix */ - public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; //$NON-NLS-1$ - /** The "xmlns" attribute name */ - public static final String XMLNS = "xmlns"; //$NON-NLS-1$ - /** The default prefix used for the {@link #XMLNS_URI} */ - public static final String XMLNS_PREFIX = "xmlns:"; //$NON-NLS-1$ - /** Qualified name of the xmlns android declaration element */ - public static final String XMLNS_ANDROID = "xmlns:android"; //$NON-NLS-1$ - /** The default prefix used for the {@link #ANDROID_URI} name space */ - public static final String ANDROID_NS_NAME = "android"; //$NON-NLS-1$ - /** The default prefix used for the {@link #ANDROID_URI} name space including the colon */ - public static final String ANDROID_NS_NAME_PREFIX = "android:"; //$NON-NLS-1$ - /** The default prefix used for the app */ - public static final String APP_PREFIX = "app"; //$NON-NLS-1$ - /** The entity for the ampersand character */ - public static final String AMP_ENTITY = "&"; //$NON-NLS-1$ - /** The entity for the quote character */ - public static final String QUOT_ENTITY = """; //$NON-NLS-1$ - /** The entity for the apostrophe character */ - public static final String APOS_ENTITY = "'"; //$NON-NLS-1$ - /** The entity for the less than character */ - public static final String LT_ENTITY = "<"; //$NON-NLS-1$ - /** The entity for the greater than character */ - public static final String GT_ENTITY = ">"; //$NON-NLS-1$ - - // ---- Elements and Attributes ---- - - /** Namespace prefix used for all resources */ - public static final String URI_PREFIX = - "http://schemas.android.com/apk/res/"; //$NON-NLS-1$ - /** Namespace used in XML files for Android attributes */ - public static final String ANDROID_URI = - "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$ - /** Namespace used in XML files for Android Tooling attributes */ - public static final String TOOLS_URI = - "http://schemas.android.com/tools"; //$NON-NLS-1$ - /** Namespace used for auto-adjusting namespaces */ - public static final String AUTO_URI = - "http://schemas.android.com/apk/res-auto"; //$NON-NLS-1$ - /** Default prefix used for tools attributes */ - public static final String TOOLS_PREFIX = "tools"; //$NON-NLS-1$ - public static final String R_CLASS = "R"; //$NON-NLS-1$ - public static final String ANDROID_PKG = "android"; //$NON-NLS-1$ - - // Tags: Manifest - public static final String TAG_SERVICE = "service"; //$NON-NLS-1$ - public static final String TAG_PERMISSION = "permission"; //$NON-NLS-1$ - public static final String TAG_USES_PERMISSION = "uses-permission";//$NON-NLS-1$ - public static final String TAG_USES_LIBRARY = "uses-library"; //$NON-NLS-1$ - public static final String TAG_APPLICATION = "application"; //$NON-NLS-1$ - public static final String TAG_INTENT_FILTER = "intent-filter"; //$NON-NLS-1$ - public static final String TAG_USES_SDK = "uses-sdk"; //$NON-NLS-1$ - public static final String TAG_ACTIVITY = "activity"; //$NON-NLS-1$ - public static final String TAG_RECEIVER = "receiver"; //$NON-NLS-1$ - public static final String TAG_PROVIDER = "provider"; //$NON-NLS-1$ - public static final String TAG_GRANT_PERMISSION = "grant-uri-permission"; //$NON-NLS-1$ - public static final String TAG_PATH_PERMISSION = "path-permission"; //$NON-NLS-1$ - - // Tags: Resources - public static final String TAG_RESOURCES = "resources"; //$NON-NLS-1$ - public static final String TAG_STRING = "string"; //$NON-NLS-1$ - public static final String TAG_ARRAY = "array"; //$NON-NLS-1$ - public static final String TAG_STYLE = "style"; //$NON-NLS-1$ - public static final String TAG_ITEM = "item"; //$NON-NLS-1$ - public static final String TAG_STRING_ARRAY = "string-array"; //$NON-NLS-1$ - public static final String TAG_PLURALS = "plurals"; //$NON-NLS-1$ - public static final String TAG_INTEGER_ARRAY = "integer-array"; //$NON-NLS-1$ - public static final String TAG_COLOR = "color"; //$NON-NLS-1$ - public static final String TAG_DIMEN = "dimen"; //$NON-NLS-1$ - public static final String TAG_DRAWABLE = "drawable"; //$NON-NLS-1$ - public static final String TAG_MENU = "menu"; //$NON-NLS-1$ - - // Tags: Layouts - public static final String VIEW_TAG = "view"; //$NON-NLS-1$ - public static final String VIEW_INCLUDE = "include"; //$NON-NLS-1$ - public static final String VIEW_MERGE = "merge"; //$NON-NLS-1$ - public static final String VIEW_FRAGMENT = "fragment"; //$NON-NLS-1$ - public static final String REQUEST_FOCUS = "requestFocus"; //$NON-NLS-1$ - - public static final String VIEW = "View"; //$NON-NLS-1$ - public static final String VIEW_GROUP = "ViewGroup"; //$NON-NLS-1$ - public static final String FRAME_LAYOUT = "FrameLayout"; //$NON-NLS-1$ - public static final String LINEAR_LAYOUT = "LinearLayout"; //$NON-NLS-1$ - public static final String RELATIVE_LAYOUT = "RelativeLayout"; //$NON-NLS-1$ - public static final String GRID_LAYOUT = "GridLayout"; //$NON-NLS-1$ - public static final String SCROLL_VIEW = "ScrollView"; //$NON-NLS-1$ - public static final String BUTTON = "Button"; //$NON-NLS-1$ - public static final String COMPOUND_BUTTON = "CompoundButton"; //$NON-NLS-1$ - public static final String ADAPTER_VIEW = "AdapterView"; //$NON-NLS-1$ - public static final String GALLERY = "Gallery"; //$NON-NLS-1$ - public static final String GRID_VIEW = "GridView"; //$NON-NLS-1$ - public static final String TAB_HOST = "TabHost"; //$NON-NLS-1$ - public static final String RADIO_GROUP = "RadioGroup"; //$NON-NLS-1$ - public static final String RADIO_BUTTON = "RadioButton"; //$NON-NLS-1$ - public static final String SWITCH = "Switch"; //$NON-NLS-1$ - public static final String EDIT_TEXT = "EditText"; //$NON-NLS-1$ - public static final String LIST_VIEW = "ListView"; //$NON-NLS-1$ - public static final String TEXT_VIEW = "TextView"; //$NON-NLS-1$ - public static final String CHECKED_TEXT_VIEW = "CheckedTextView"; //$NON-NLS-1$ - public static final String IMAGE_VIEW = "ImageView"; //$NON-NLS-1$ - public static final String SURFACE_VIEW = "SurfaceView"; //$NON-NLS-1$ - public static final String ABSOLUTE_LAYOUT = "AbsoluteLayout"; //$NON-NLS-1$ - public static final String TABLE_LAYOUT = "TableLayout"; //$NON-NLS-1$ - public static final String TABLE_ROW = "TableRow"; //$NON-NLS-1$ - public static final String TAB_WIDGET = "TabWidget"; //$NON-NLS-1$ - public static final String IMAGE_BUTTON = "ImageButton"; //$NON-NLS-1$ - public static final String SEEK_BAR = "SeekBar"; //$NON-NLS-1$ - public static final String VIEW_STUB = "ViewStub"; //$NON-NLS-1$ - public static final String SPINNER = "Spinner"; //$NON-NLS-1$ - public static final String WEB_VIEW = "WebView"; //$NON-NLS-1$ - public static final String TOGGLE_BUTTON = "ToggleButton"; //$NON-NLS-1$ - public static final String CHECK_BOX = "CheckBox"; //$NON-NLS-1$ - public static final String ABS_LIST_VIEW = "AbsListView"; //$NON-NLS-1$ - public static final String PROGRESS_BAR = "ProgressBar"; //$NON-NLS-1$ - public static final String ABS_SPINNER = "AbsSpinner"; //$NON-NLS-1$ - public static final String ABS_SEEK_BAR = "AbsSeekBar"; //$NON-NLS-1$ - public static final String VIEW_ANIMATOR = "ViewAnimator"; //$NON-NLS-1$ - public static final String VIEW_SWITCHER = "ViewSwitcher"; //$NON-NLS-1$ - public static final String EXPANDABLE_LIST_VIEW = "ExpandableListView"; //$NON-NLS-1$ - public static final String HORIZONTAL_SCROLL_VIEW = "HorizontalScrollView"; //$NON-NLS-1$ - public static final String MULTI_AUTO_COMPLETE_TEXT_VIEW = "MultiAutoCompleteTextView"; //$NON-NLS-1$ - public static final String AUTO_COMPLETE_TEXT_VIEW = "AutoCompleteTextView"; //$NON-NLS-1$ - public static final String CHECKABLE = "Checkable"; //$NON-NLS-1$ - - // Tags: Drawables - public static final String TAG_BITMAP = "bitmap"; //$NON-NLS-1$ - - // Attributes: Manifest - public static final String ATTR_EXPORTED = "exported"; //$NON-NLS-1$ - public static final String ATTR_PERMISSION = "permission"; //$NON-NLS-1$ - public static final String ATTR_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-1$ - public static final String ATTR_TARGET_SDK_VERSION = "targetSdkVersion"; //$NON-NLS-1$ - public static final String ATTR_ICON = "icon"; //$NON-NLS-1$ - public static final String ATTR_PACKAGE = "package"; //$NON-NLS-1$ - public static final String ATTR_CORE_APP = "coreApp"; //$NON-NLS-1$ - public static final String ATTR_THEME = "theme"; //$NON-NLS-1$ - public static final String ATTR_PATH = "path"; //$NON-NLS-1$ - public static final String ATTR_PATH_PREFIX = "pathPrefix"; //$NON-NLS-1$ - public static final String ATTR_PATH_PATTERN = "pathPattern"; //$NON-NLS-1$ - public static final String ATTR_ALLOW_BACKUP = "allowBackup"; //$NON_NLS-1$ - public static final String ATTR_DEBUGGABLE = "debuggable"; //$NON-NLS-1$ - public static final String ATTR_READ_PERMISSION = "readPermission"; //$NON_NLS-1$ - public static final String ATTR_WRITE_PERMISSION = "writePermission"; //$NON_NLS-1$ - - // Attributes: Resources - public static final String ATTR_NAME = "name"; //$NON-NLS-1$ - public static final String ATTR_TYPE = "type"; //$NON-NLS-1$ - public static final String ATTR_PARENT = "parent"; //$NON-NLS-1$ - public static final String ATTR_TRANSLATABLE = "translatable"; //$NON-NLS-1$ - public static final String ATTR_COLOR = "color"; //$NON-NLS-1$ - - // Attributes: Layout - public static final String ATTR_LAYOUT_RESOURCE_PREFIX = "layout_";//$NON-NLS-1$ - public static final String ATTR_CLASS = "class"; //$NON-NLS-1$ - public static final String ATTR_STYLE = "style"; //$NON-NLS-1$ - public static final String ATTR_CONTEXT = "context"; //$NON-NLS-1$ - public static final String ATTR_ID = "id"; //$NON-NLS-1$ - public static final String ATTR_TEXT = "text"; //$NON-NLS-1$ - public static final String ATTR_TEXT_SIZE = "textSize"; //$NON-NLS-1$ - public static final String ATTR_LABEL = "label"; //$NON-NLS-1$ - public static final String ATTR_HINT = "hint"; //$NON-NLS-1$ - public static final String ATTR_PROMPT = "prompt"; //$NON-NLS-1$ - public static final String ATTR_ON_CLICK = "onClick"; //$NON-NLS-1$ - public static final String ATTR_INPUT_TYPE = "inputType"; //$NON-NLS-1$ - public static final String ATTR_INPUT_METHOD = "inputMethod"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_GRAVITY = "layout_gravity"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_WIDTH = "layout_width"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_HEIGHT = "layout_height"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_WEIGHT = "layout_weight"; //$NON-NLS-1$ - public static final String ATTR_PADDING = "padding"; //$NON-NLS-1$ - public static final String ATTR_PADDING_BOTTOM = "paddingBottom"; //$NON-NLS-1$ - public static final String ATTR_PADDING_TOP = "paddingTop"; //$NON-NLS-1$ - public static final String ATTR_PADDING_RIGHT = "paddingRight"; //$NON-NLS-1$ - public static final String ATTR_PADDING_LEFT = "paddingLeft"; //$NON-NLS-1$ - public static final String ATTR_FOREGROUND = "foreground"; //$NON-NLS-1$ - public static final String ATTR_BACKGROUND = "background"; //$NON-NLS-1$ - public static final String ATTR_ORIENTATION = "orientation"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT = "layout"; //$NON-NLS-1$ - public static final String ATTR_ROW_COUNT = "rowCount"; //$NON-NLS-1$ - public static final String ATTR_COLUMN_COUNT = "columnCount"; //$NON-NLS-1$ - public static final String ATTR_LABEL_FOR = "labelFor"; //$NON-NLS-1$ - public static final String ATTR_BASELINE_ALIGNED = "baselineAligned"; //$NON-NLS-1$ - public static final String ATTR_CONTENT_DESCRIPTION = "contentDescription"; //$NON-NLS-1$ - public static final String ATTR_IME_ACTION_LABEL = "imeActionLabel"; //$NON-NLS-1$ - public static final String ATTR_PRIVATE_IME_OPTIONS = "privateImeOptions"; //$NON-NLS-1$ - public static final String VALUE_NONE = "none"; //$NON-NLS-1$ - public static final String VALUE_NO = "no"; //$NON-NLS-1$ - public static final String ATTR_NUMERIC = "numeric"; //$NON-NLS-1$ - public static final String ATTR_IME_ACTION_ID = "imeActionId"; //$NON-NLS-1$ - public static final String ATTR_IME_OPTIONS = "imeOptions"; //$NON-NLS-1$ - public static final String ATTR_FREEZES_TEXT = "freezesText"; //$NON-NLS-1$ - public static final String ATTR_EDITOR_EXTRAS = "editorExtras"; //$NON-NLS-1$ - public static final String ATTR_EDITABLE = "editable"; //$NON-NLS-1$ - public static final String ATTR_DIGITS = "digits"; //$NON-NLS-1$ - public static final String ATTR_CURSOR_VISIBLE = "cursorVisible"; //$NON-NLS-1$ - public static final String ATTR_CAPITALIZE = "capitalize"; //$NON-NLS-1$ - public static final String ATTR_PHONE_NUMBER = "phoneNumber"; //$NON-NLS-1$ - public static final String ATTR_PASSWORD = "password"; //$NON-NLS-1$ - public static final String ATTR_BUFFER_TYPE = "bufferType"; //$NON-NLS-1$ - public static final String ATTR_AUTO_TEXT = "autoText"; //$NON-NLS-1$ - public static final String ATTR_ENABLED = "enabled"; //$NON-NLS-1$ - public static final String ATTR_SINGLE_LINE = "singleLine"; //$NON-NLS-1$ - public static final String ATTR_SCALE_TYPE = "scaleType"; //$NON-NLS-1$ - public static final String ATTR_VISIBILITY = "visibility"; //$NON-NLS-1$ - public static final String ATTR_TEXT_IS_SELECTABLE = - "textIsSelectable"; //$NON-NLS-1$ - public static final String ATTR_IMPORTANT_FOR_ACCESSIBILITY = - "importantForAccessibility"; //$NON-NLS-1$ - - // AbsoluteLayout layout params - public static final String ATTR_LAYOUT_Y = "layout_y"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_X = "layout_x"; //$NON-NLS-1$ - - // GridLayout layout params - public static final String ATTR_LAYOUT_ROW = "layout_row"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_ROW_SPAN = "layout_rowSpan";//$NON-NLS-1$ - public static final String ATTR_LAYOUT_COLUMN = "layout_column"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_COLUMN_SPAN = "layout_columnSpan"; //$NON-NLS-1$ - - // TableRow - public static final String ATTR_LAYOUT_SPAN = "layout_span"; //$NON-NLS-1$ - - // RelativeLayout layout params: - public static final String ATTR_LAYOUT_ALIGN_LEFT = "layout_alignLeft"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_ALIGN_RIGHT = "layout_alignRight"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_ALIGN_TOP = "layout_alignTop"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_ALIGN_BOTTOM = "layout_alignBottom"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_ALIGN_PARENT_TOP = "layout_alignParentTop"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_ALIGN_PARENT_BOTTOM = "layout_alignParentBottom"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_ALIGN_PARENT_LEFT = "layout_alignParentLeft";//$NON-NLS-1$ - public static final String ATTR_LAYOUT_ALIGN_PARENT_RIGHT = "layout_alignParentRight"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING = "layout_alignWithParentIfMissing"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_ALIGN_BASELINE = "layout_alignBaseline"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_CENTER_IN_PARENT = "layout_centerInParent"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_CENTER_VERTICAL = "layout_centerVertical"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_CENTER_HORIZONTAL = "layout_centerHorizontal"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_TO_RIGHT_OF = "layout_toRightOf"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_TO_LEFT_OF = "layout_toLeftOf"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_BELOW = "layout_below"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_ABOVE = "layout_above"; //$NON-NLS-1$ - - // Margins - public static final String ATTR_LAYOUT_MARGIN = "layout_margin"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_MARGIN_LEFT = "layout_marginLeft"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_MARGIN_RIGHT = "layout_marginRight"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_MARGIN_TOP = "layout_marginTop"; //$NON-NLS-1$ - public static final String ATTR_LAYOUT_MARGIN_BOTTOM = "layout_marginBottom"; //$NON-NLS-1$ - - // Attributes: Drawables - public static final String ATTR_TILE_MODE = "tileMode"; //$NON-NLS-1$ - - // Values: Layouts - public static final String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$ - public static final String VALUE_MATCH_PARENT = "match_parent"; //$NON-NLS-1$ - public static final String VALUE_VERTICAL = "vertical"; //$NON-NLS-1$ - public static final String VALUE_TRUE = "true"; //$NON-NLS-1$ - public static final String VALUE_EDITABLE = "editable"; //$NON-NLS-1$ - public static final String VALUE_AUTO_FIT = "auto_fit"; //$NON-NLS-1$ - public static final String VALUE_SELECTABLE_ITEM_BACKGROUND = - "?android:attr/selectableItemBackground"; //$NON-NLS-1$ - - - // Values: Resources - public static final String VALUE_ID = "id"; //$NON-NLS-1$ - - // Values: Drawables - public static final String VALUE_DISABLED = "disabled"; //$NON-NLS-1$ - public static final String VALUE_CLAMP = "clamp"; //$NON-NLS-1$ - - // Menus - public static final String ATTR_SHOW_AS_ACTION = "showAsAction"; //$NON-NLS-1$ - public static final String ATTR_TITLE = "title"; //$NON-NLS-1$ - public static final String ATTR_VISIBLE = "visible"; //$NON-NLS-1$ - public static final String VALUE_IF_ROOM = "ifRoom"; //$NON-NLS-1$ - public static final String VALUE_ALWAYS = "always"; //$NON-NLS-1$ - - // Units - public static final String UNIT_DP = "dp"; //$NON-NLS-1$ - public static final String UNIT_DIP = "dip"; //$NON-NLS-1$ - public static final String UNIT_SP = "sp"; //$NON-NLS-1$ - public static final String UNIT_PX = "px"; //$NON-NLS-1$ - public static final String UNIT_IN = "in"; //$NON-NLS-1$ - public static final String UNIT_MM = "mm"; //$NON-NLS-1$ - public static final String UNIT_PT = "pt"; //$NON-NLS-1$ - - // Filenames and folder names - public static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml"; //$NON-NLS-1$ - public static final String OLD_PROGUARD_FILE = "proguard.cfg"; //$NON-NLS-1$ - public static final String CLASS_FOLDER = - "bin" + File.separator + "classes"; //$NON-NLS-1$ //$NON-NLS-2$ - public static final String GEN_FOLDER = "gen"; //$NON-NLS-1$ - public static final String SRC_FOLDER = "src"; //$NON-NLS-1$ - public static final String LIBS_FOLDER = "libs"; //$NON-NLS-1$ - public static final String BIN_FOLDER = "bin"; //$NON-NLS-1$ - - public static final String RES_FOLDER = "res"; //$NON-NLS-1$ - public static final String DOT_XML = ".xml"; //$NON-NLS-1$ - public static final String DOT_GIF = ".gif"; //$NON-NLS-1$ - public static final String DOT_JPG = ".jpg"; //$NON-NLS-1$ - public static final String DOT_JPEG = ".jpeg"; //$NON-NLS-1$ - public static final String DOT_PNG = ".png"; //$NON-NLS-1$ - public static final String DOT_9PNG = ".9.png"; //$NON-NLS-1$ - public static final String DOT_JAVA = ".java"; //$NON-NLS-1$ - public static final String DOT_CLASS = ".class"; //$NON-NLS-1$ - public static final String DOT_JAR = ".jar"; //$NON-NLS-1$ - - - /** Extension of the Application package Files, i.e. "apk". */ - public static final String EXT_ANDROID_PACKAGE = "apk"; //$NON-NLS-1$ - /** Extension of java files, i.e. "java" */ - public static final String EXT_JAVA = "java"; //$NON-NLS-1$ - /** Extension of compiled java files, i.e. "class" */ - public static final String EXT_CLASS = "class"; //$NON-NLS-1$ - /** Extension of xml files, i.e. "xml" */ - public static final String EXT_XML = "xml"; //$NON-NLS-1$ - /** Extension of jar files, i.e. "jar" */ - public static final String EXT_JAR = "jar"; //$NON-NLS-1$ - /** Extension of aidl files, i.e. "aidl" */ - public static final String EXT_AIDL = "aidl"; //$NON-NLS-1$ - /** Extension of Renderscript files, i.e. "rs" */ - public static final String EXT_RS = "rs"; //$NON-NLS-1$ - /** Extension of FilterScript files, i.e. "fs" */ - public static final String EXT_FS = "fs"; //$NON-NLS-1$ - /** Extension of dependency files, i.e. "d" */ - public static final String EXT_DEP = "d"; //$NON-NLS-1$ - /** Extension of native libraries, i.e. "so" */ - public static final String EXT_NATIVE_LIB = "so"; //$NON-NLS-1$ - /** Extension of dex files, i.e. "dex" */ - public static final String EXT_DEX = "dex"; //$NON-NLS-1$ - /** Extension for temporary resource files, ie "ap_ */ - public static final String EXT_RES = "ap_"; //$NON-NLS-1$ - /** Extension for pre-processable images. Right now pngs */ - public static final String EXT_PNG = "png"; //$NON-NLS-1$ - - private final static String DOT = "."; //$NON-NLS-1$ - - /** Dot-Extension of the Application package Files, i.e. ".apk". */ - public static final String DOT_ANDROID_PACKAGE = DOT + EXT_ANDROID_PACKAGE; - /** Dot-Extension of aidl files, i.e. ".aidl" */ - public static final String DOT_AIDL = DOT + EXT_AIDL; - /** Dot-Extension of renderscript files, i.e. ".rs" */ - public static final String DOT_RS = DOT + EXT_RS; - /** Dot-Extension of FilterScript files, i.e. ".fs" */ - public static final String DOT_FS = DOT + EXT_FS; - /** Dot-Extension of dependency files, i.e. ".d" */ - public static final String DOT_DEP = DOT + EXT_DEP; - /** Dot-Extension of dex files, i.e. ".dex" */ - public static final String DOT_DEX = DOT + EXT_DEX; - /** Dot-Extension for temporary resource files, ie "ap_ */ - public static final String DOT_RES = DOT + EXT_RES; - /** Dot-Extension for BMP files, i.e. ".bmp" */ - public static final String DOT_BMP = ".bmp"; //$NON-NLS-1$ - /** Dot-Extension for SVG files, i.e. ".svg" */ - public static final String DOT_SVG = ".svg"; //$NON-NLS-1$ - /** Dot-Extension for template files */ - public static final String DOT_FTL = ".ftl"; //$NON-NLS-1$ - /** Dot-Extension of text files, i.e. ".txt" */ - public static final String DOT_TXT = ".txt"; //$NON-NLS-1$ - - /** Resource base name for java files and classes */ - public static final String FN_RESOURCE_BASE = "R"; //$NON-NLS-1$ - /** Resource java class filename, i.e. "R.java" */ - public static final String FN_RESOURCE_CLASS = FN_RESOURCE_BASE + DOT_JAVA; - /** Resource class file filename, i.e. "R.class" */ - public static final String FN_COMPILED_RESOURCE_CLASS = FN_RESOURCE_BASE + DOT_CLASS; - /** Resource text filename, i.e. "R.txt" */ - public static final String FN_RESOURCE_TEXT = FN_RESOURCE_BASE + DOT_TXT; - /** Generated manifest class name */ - public static final String FN_MANIFEST_BASE = "Manifest"; //$NON-NLS-1$ - /** Generated BuildConfig class name */ - public static final String FN_BUILD_CONFIG_BASE = "BuildConfig"; //$NON-NLS-1$ - /** Manifest java class filename, i.e. "Manifest.java" */ - public static final String FN_MANIFEST_CLASS = FN_MANIFEST_BASE + DOT_JAVA; - /** BuildConfig java class filename, i.e. "BuildConfig.java" */ - public static final String FN_BUILD_CONFIG = FN_BUILD_CONFIG_BASE + DOT_JAVA; - - public static final String DRAWABLE_FOLDER = "drawable"; //$NON-NLS-1$ - public static final String DRAWABLE_XHDPI = "drawable-xhdpi"; //$NON-NLS-1$ - public static final String DRAWABLE_HDPI = "drawable-hdpi"; //$NON-NLS-1$ - public static final String DRAWABLE_MDPI = "drawable-mdpi"; //$NON-NLS-1$ - public static final String DRAWABLE_LDPI = "drawable-ldpi"; //$NON-NLS-1$ - - // Resources - public static final String PREFIX_RESOURCE_REF = "@"; //$NON-NLS-1$ - public static final String PREFIX_THEME_REF = "?"; //$NON-NLS-1$ - public static final String ANDROID_PREFIX = "@android:"; //$NON-NLS-1$ - public static final String ANDROID_THEME_PREFIX = "?android:"; //$NON-NLS-1$ - public static final String LAYOUT_RESOURCE_PREFIX = "@layout/"; //$NON-NLS-1$ - public static final String STYLE_RESOURCE_PREFIX = "@style/"; //$NON-NLS-1$ - public static final String NEW_ID_PREFIX = "@+id/"; //$NON-NLS-1$ - public static final String ID_PREFIX = "@id/"; //$NON-NLS-1$ - public static final String DRAWABLE_PREFIX = "@drawable/"; //$NON-NLS-1$ - public static final String STRING_PREFIX = "@string/"; //$NON-NLS-1$ - public static final String ANDROID_STRING_PREFIX = "@android:string/"; //$NON-NLS-1$ - public static final String ANDROID_LAYOUT_RESOURCE_PREFIX = "@android:layout/"; //$NON-NLS-1$ - - public static final String RESOURCE_CLZ_ID = "id"; //$NON-NLS-1$ - public static final String RESOURCE_CLZ_COLOR = "color"; //$NON-NLS-1$ - public static final String RESOURCE_CLZ_ARRAY = "array"; //$NON-NLS-1$ - public static final String RESOURCE_CLZ_ATTR = "attr"; //$NON-NLS-1$ - public static final String RESOURCE_CLR_STYLEABLE = "styleable"; //$NON-NLS-1$ - public static final String NULL_RESOURCE = "@null"; //$NON-NLS-1$ - public static final String TRANSPARENT_COLOR = "@android:color/transparent"; //$NON-NLS-1$ - public static final String ANDROID_STYLE_RESOURCE_PREFIX = "@android:style/"; //$NON-NLS-1$ - public static final String REFERENCE_STYLE = "style/"; //$NON-NLS-1$ - public static final String PREFIX_ANDROID = "android:"; //$NON-NLS-1$ - - // Resource Types - public static final String DRAWABLE_TYPE = "drawable"; //$NON-NLS-1$ - public static final String MENU_TYPE = "menu"; //$NON-NLS-1$ - - // Packages - public static final String ANDROID_PKG_PREFIX = "android."; //$NON-NLS-1$ - public static final String WIDGET_PKG_PREFIX = "android.widget."; //$NON-NLS-1$ - public static final String VIEW_PKG_PREFIX = "android.view."; //$NON-NLS-1$ - - // Project properties - public static final String ANDROID_LIBRARY = "android.library"; //$NON-NLS-1$ - public static final String PROGUARD_CONFIG = "proguard.config"; //$NON-NLS-1$ - public static final String ANDROID_LIBRARY_REFERENCE_FORMAT = "android.library.reference.%1$d";//$NON-NLS-1$ - public static final String PROJECT_PROPERTIES = "project.properties";//$NON-NLS-1$ - - // Java References - public static final String ATTR_REF_PREFIX = "?attr/"; //$NON-NLS-1$ - public static final String R_PREFIX = "R."; //$NON-NLS-1$ - public static final String R_ID_PREFIX = "R.id."; //$NON-NLS-1$ - public static final String R_LAYOUT_RESOURCE_PREFIX = "R.layout."; //$NON-NLS-1$ - public static final String R_DRAWABLE_PREFIX = "R.drawable."; //$NON-NLS-1$ - public static final String R_ATTR_PREFIX = "R.attr."; //$NON-NLS-1$ - - // Attributes related to tools - public static final String ATTR_IGNORE = "ignore"; //$NON-NLS-1$ - public static final String ATTR_LOCALE = "locale"; //$NON-NLS-1$ - - // SuppressLint - public static final String SUPPRESS_ALL = "all"; //$NON-NLS-1$ - public static final String SUPPRESS_LINT = "SuppressLint"; //$NON-NLS-1$ - public static final String TARGET_API = "TargetApi"; //$NON-NLS-1$ - public static final String ATTR_TARGET_API = "targetApi"; //$NON-NLS-1$ - public static final String FQCN_SUPPRESS_LINT = "android.annotation." + SUPPRESS_LINT; //$NON-NLS-1$ - public static final String FQCN_TARGET_API = "android.annotation." + TARGET_API; //$NON-NLS-1$ - - // Class Names - public static final String CONSTRUCTOR_NAME = "<init>"; //$NON-NLS-1$ - public static final String CLASS_CONSTRUCTOR = "<clinit>"; //$NON-NLS-1$ - public static final String FRAGMENT = "android/app/Fragment"; //$NON-NLS-1$ - public static final String FRAGMENT_V4 = "android/support/v4/app/Fragment"; //$NON-NLS-1$ - public static final String ANDROID_APP_ACTIVITY = "android/app/Activity"; //$NON-NLS-1$ - public static final String ANDROID_APP_SERVICE = "android/app/Service"; //$NON-NLS-1$ - public static final String ANDROID_CONTENT_CONTENT_PROVIDER = - "android/content/ContentProvider"; //$NON-NLS-1$ - public static final String ANDROID_CONTENT_BROADCAST_RECEIVER = - "android/content/BroadcastReceiver"; //$NON-NLS-1$ - - // Method Names - public static final String FORMAT_METHOD = "format"; //$NON-NLS-1$ - public static final String GET_STRING_METHOD = "getString"; //$NON-NLS-1$ - - - - - public static final String ATTR_TAG = "tag"; //$NON-NLS-1$ - public static final String ATTR_NUM_COLUMNS = "numColumns"; //$NON-NLS-1$ - - // Some common layout element names - public static final String CALENDAR_VIEW = "CalendarView"; //$NON-NLS-1$ - public static final String SPACE = "Space"; //$NON-NLS-1$ - public static final String GESTURE_OVERLAY_VIEW = "GestureOverlayView";//$NON-NLS-1$ - - public static final String ATTR_HANDLE = "handle"; //$NON-NLS-1$ - public static final String ATTR_CONTENT = "content"; //$NON-NLS-1$ - public static final String ATTR_CHECKED = "checked"; //$NON-NLS-1$ - - // TextView - public static final String ATTR_DRAWABLE_RIGHT = "drawableRight"; //$NON-NLS-1$ - public static final String ATTR_DRAWABLE_LEFT = "drawableLeft"; //$NON-NLS-1$ - public static final String ATTR_DRAWABLE_BOTTOM = "drawableBottom"; //$NON-NLS-1$ - public static final String ATTR_DRAWABLE_TOP = "drawableTop"; //$NON-NLS-1$ - public static final String ATTR_DRAWABLE_PADDING = "drawablePadding"; //$NON-NLS-1$ - - public static final String ATTR_USE_DEFAULT_MARGINS = "useDefaultMargins"; //$NON-NLS-1$ - public static final String ATTR_MARGINS_INCLUDED_IN_ALIGNMENT = "marginsIncludedInAlignment"; //$NON-NLS-1$ - - public static final String VALUE_WRAP_CONTENT = "wrap_content"; //$NON-NLS-1$ - public static final String VALUE_FALSE= "false"; //$NON-NLS-1$ - public static final String VALUE_N_DP = "%ddp"; //$NON-NLS-1$ - public static final String VALUE_ZERO_DP = "0dp"; //$NON-NLS-1$ - public static final String VALUE_ONE_DP = "1dp"; //$NON-NLS-1$ - public static final String VALUE_TOP = "top"; //$NON-NLS-1$ - public static final String VALUE_LEFT = "left"; //$NON-NLS-1$ - public static final String VALUE_RIGHT = "right"; //$NON-NLS-1$ - public static final String VALUE_BOTTOM = "bottom"; //$NON-NLS-1$ - public static final String VALUE_CENTER_VERTICAL = "center_vertical"; //$NON-NLS-1$ - public static final String VALUE_CENTER_HORIZONTAL = "center_horizontal"; //$NON-NLS-1$ - public static final String VALUE_FILL_HORIZONTAL = "fill_horizontal"; //$NON-NLS-1$ - public static final String VALUE_FILL_VERTICAL = "fill_vertical"; //$NON-NLS-1$ - public static final String VALUE_0 = "0"; //$NON-NLS-1$ - public static final String VALUE_1 = "1"; //$NON-NLS-1$ - - // Gravity values. These have the GRAVITY_ prefix in front of value because we already - // have VALUE_CENTER_HORIZONTAL defined for layouts, and its definition conflicts - // (centerHorizontal versus center_horizontal) - public static final String GRAVITY_VALUE_ = "center"; //$NON-NLS-1$ - public static final String GRAVITY_VALUE_CENTER = "center"; //$NON-NLS-1$ - public static final String GRAVITY_VALUE_RIGHT = "right"; //$NON-NLS-1$ - public static final String GRAVITY_VALUE_LEFT = "left"; //$NON-NLS-1$ - public static final String GRAVITY_VALUE_BOTTOM = "bottom"; //$NON-NLS-1$ - public static final String GRAVITY_VALUE_TOP = "top"; //$NON-NLS-1$ - public static final String GRAVITY_VALUE_FILL_HORIZONTAL = "fill_horizontal"; //$NON-NLS-1$ - public static final String GRAVITY_VALUE_FILL_VERTICAL = "fill_vertical"; //$NON-NLS-1$ - public static final String GRAVITY_VALUE_CENTER_HORIZONTAL = "center_horizontal"; //$NON-NLS-1$ - public static final String GRAVITY_VALUE_CENTER_VERTICAL = "center_vertical"; //$NON-NLS-1$ - public static final String GRAVITY_VALUE_FILL = "fill"; //$NON-NLS-1$ - - /** - * The top level android package as a prefix, "android.". - */ - public static final String ANDROID_SUPPORT_PKG_PREFIX = ANDROID_PKG_PREFIX + "support."; //$NON-NLS-1$ - - /** The android.view. package prefix */ - public static final String ANDROID_VIEW_PKG = ANDROID_PKG_PREFIX + "view."; //$NON-NLS-1$ - - /** The android.widget. package prefix */ - public static final String ANDROID_WIDGET_PREFIX = ANDROID_PKG_PREFIX + "widget."; //$NON-NLS-1$ - - /** The android.webkit. package prefix */ - public static final String ANDROID_WEBKIT_PKG = ANDROID_PKG_PREFIX + "webkit."; //$NON-NLS-1$ - - /** The LayoutParams inner-class name suffix, .LayoutParams */ - public static final String DOT_LAYOUT_PARAMS = ".LayoutParams"; //$NON-NLS-1$ - - /** The fully qualified class name of an EditText view */ - public static final String FQCN_EDIT_TEXT = "android.widget.EditText"; //$NON-NLS-1$ - - /** The fully qualified class name of a LinearLayout view */ - public static final String FQCN_LINEAR_LAYOUT = "android.widget.LinearLayout"; //$NON-NLS-1$ - - /** The fully qualified class name of a RelativeLayout view */ - public static final String FQCN_RELATIVE_LAYOUT = "android.widget.RelativeLayout"; //$NON-NLS-1$ - - /** The fully qualified class name of a RelativeLayout view */ - public static final String FQCN_GRID_LAYOUT = "android.widget.GridLayout"; //$NON-NLS-1$ - public static final String FQCN_GRID_LAYOUT_V7 = "android.support.v7.widget.GridLayout"; //$NON-NLS-1$ - - /** The fully qualified class name of a FrameLayout view */ - public static final String FQCN_FRAME_LAYOUT = "android.widget.FrameLayout"; //$NON-NLS-1$ - - /** The fully qualified class name of a TableRow view */ - public static final String FQCN_TABLE_ROW = "android.widget.TableRow"; //$NON-NLS-1$ - - /** The fully qualified class name of a TableLayout view */ - public static final String FQCN_TABLE_LAYOUT = "android.widget.TableLayout"; //$NON-NLS-1$ - - /** The fully qualified class name of a GridView view */ - public static final String FQCN_GRID_VIEW = "android.widget.GridView"; //$NON-NLS-1$ - - /** The fully qualified class name of a TabWidget view */ - public static final String FQCN_TAB_WIDGET = "android.widget.TabWidget"; //$NON-NLS-1$ - - /** The fully qualified class name of a Button view */ - public static final String FQCN_BUTTON = "android.widget.Button"; //$NON-NLS-1$ - - /** The fully qualified class name of a RadioButton view */ - public static final String FQCN_RADIO_BUTTON = "android.widget.RadioButton"; //$NON-NLS-1$ - - /** The fully qualified class name of a ToggleButton view */ - public static final String FQCN_TOGGLE_BUTTON = "android.widget.ToggleButton"; //$NON-NLS-1$ - - /** The fully qualified class name of a Spinner view */ - public static final String FQCN_SPINNER = "android.widget.Spinner"; //$NON-NLS-1$ - - /** The fully qualified class name of an AdapterView */ - public static final String FQCN_ADAPTER_VIEW = "android.widget.AdapterView"; //$NON-NLS-1$ - - /** The fully qualified class name of a ListView */ - public static final String FQCN_LIST_VIEW = "android.widget.ListView"; //$NON-NLS-1$ - - /** The fully qualified class name of an ExpandableListView */ - public static final String FQCN_EXPANDABLE_LIST_VIEW = "android.widget.ExpandableListView"; //$NON-NLS-1$ - - /** The fully qualified class name of a GestureOverlayView */ - public static final String FQCN_GESTURE_OVERLAY_VIEW = "android.gesture.GestureOverlayView"; //$NON-NLS-1$ - - /** The fully qualified class name of a DatePicker */ - public static final String FQCN_DATE_PICKER = "android.widget.DatePicker"; //$NON-NLS-1$ - - /** The fully qualified class name of a TimePicker */ - public static final String FQCN_TIME_PICKER = "android.widget.TimePicker"; //$NON-NLS-1$ - - /** The fully qualified class name of a RadioGroup */ - public static final String FQCN_RADIO_GROUP = "android.widgets.RadioGroup"; //$NON-NLS-1$ - - /** The fully qualified class name of a Space */ - public static final String FQCN_SPACE = "android.widget.Space"; //$NON-NLS-1$ - public static final String FQCN_SPACE_V7 = "android.support.v7.widget.Space"; //$NON-NLS-1$ - - /** The fully qualified class name of a TextView view */ - public static final String FQCN_TEXT_VIEW = "android.widget.TextView"; //$NON-NLS-1$ - - /** The fully qualified class name of an ImageView view */ - public static final String FQCN_IMAGE_VIEW = "android.widget.ImageView"; //$NON-NLS-1$ - - public static final String ATTR_SRC = "src"; //$NON-NLS-1$ - - public static final String ATTR_GRAVITY = "gravity"; //$NON-NLS-1$ - public static final String ATTR_WEIGHT_SUM = "weightSum"; //$NON-NLS-1$ - public static final String ATTR_EMS = "ems"; //$NON-NLS-1$ - - public static final String VALUE_HORIZONTAL = "horizontal"; //$NON-NLS-1$ - - /** - * The highest known API level. Note that the tools may also look at the - * installed platforms to see if they can find more recently released - * platforms, e.g. when the tools have not yet been updated for a new - * release. This number is used as a baseline and any more recent platforms - * found can be used to increase the highest known number. - */ - public static final int HIGHEST_KNOWN_API = 17; -} diff --git a/common/src/main/java/com/android/annotations/NonNull.java b/common/src/main/java/com/android/annotations/NonNull.java deleted file mode 100644 index 973ebb6..0000000 --- a/common/src/main/java/com/android/annotations/NonNull.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.annotations; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.LOCAL_VARIABLE; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Denotes that a parameter, field or method return value can never be null. - * <p/> - * This is a marker annotation and it has no specific attributes. - */ -@Documented -@Retention(RetentionPolicy.CLASS) -@Target({METHOD,PARAMETER,LOCAL_VARIABLE,FIELD}) -public @interface NonNull { -} diff --git a/common/src/main/java/com/android/annotations/NonNullByDefault.java b/common/src/main/java/com/android/annotations/NonNullByDefault.java deleted file mode 100644 index 3db891c..0000000 --- a/common/src/main/java/com/android/annotations/NonNullByDefault.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.annotations; - -import static java.lang.annotation.ElementType.PACKAGE; -import static java.lang.annotation.ElementType.TYPE; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Denotes that all parameters, fields or methods within a class or method by - * default can not be null. This can be overridden by adding specific - * {@link com.android.annotations.Nullable} annotations on fields, parameters or - * methods that should not use the default. - * <p/> - * NOTE: Eclipse does not yet handle defaults well (in particular, if - * you add this on a class which implements Comparable, then it will insist - * that your compare method is changing the nullness of the compare parameter, - * so you'll need to add @Nullable on it, which also is not right (since - * the method should have implied @NonNull and you do not need to check - * the parameter.). For now, it's best to individually annotate methods, - * parameters and fields. - * <p/> - * This is a marker annotation and it has no specific attributes. - */ -@Documented -@Retention(RetentionPolicy.CLASS) -@Target({PACKAGE, TYPE}) -public @interface NonNullByDefault { -} diff --git a/common/src/main/java/com/android/annotations/Nullable.java b/common/src/main/java/com/android/annotations/Nullable.java deleted file mode 100755 index d9c3861..0000000 --- a/common/src/main/java/com/android/annotations/Nullable.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.annotations; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.LOCAL_VARIABLE; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Denotes that a parameter, field or method return value can be null. - * <b>Note</b>: this is the default assumption for most Java APIs and the - * default assumption made by most static code checking tools, so usually you - * don't need to use this annotation; its primary use is to override a default - * wider annotation like {@link NonNullByDefault}. - * <p/> - * When decorating a method call parameter, this denotes the parameter can - * legitimately be null and the method will gracefully deal with it. Typically - * used on optional parameters. - * <p/> - * When decorating a method, this denotes the method might legitimately return - * null. - * <p/> - * This is a marker annotation and it has no specific attributes. - */ -@Documented -@Retention(RetentionPolicy.CLASS) -@Target({METHOD, PARAMETER, LOCAL_VARIABLE, FIELD}) -public @interface Nullable { -} diff --git a/common/src/main/java/com/android/annotations/VisibleForTesting.java b/common/src/main/java/com/android/annotations/VisibleForTesting.java deleted file mode 100755 index 7f41d70..0000000 --- a/common/src/main/java/com/android/annotations/VisibleForTesting.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Denotes that the class, method or field has its visibility relaxed so - * that unit tests can access it. - * <p/> - * The <code>visibility</code> argument can be used to specific what the original - * visibility should have been if it had not been made public or package-private for testing. - * The default is to consider the element private. - */ -@Retention(RetentionPolicy.SOURCE) -public @interface VisibleForTesting { - /** - * Intended visibility if the element had not been made public or package-private for - * testing. - */ - enum Visibility { - /** The element should be considered protected. */ - PROTECTED, - /** The element should be considered package-private. */ - PACKAGE, - /** The element should be considered private. */ - PRIVATE - } - - /** - * Intended visibility if the element had not been made public or package-private for testing. - * If not specified, one should assume the element originally intended to be private. - */ - Visibility visibility() default Visibility.PRIVATE; -} diff --git a/common/src/main/java/com/android/io/FileWrapper.java b/common/src/main/java/com/android/io/FileWrapper.java deleted file mode 100644 index 8be7859..0000000 --- a/common/src/main/java/com/android/io/FileWrapper.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2008 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.io; - - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; - -/** - * An implementation of {@link IAbstractFile} extending {@link File}. - */ -public class FileWrapper extends File implements IAbstractFile { - private static final long serialVersionUID = 1L; - - /** - * Creates a new File instance matching a given {@link File} object. - * @param file the file to match - */ - public FileWrapper(File file) { - super(file.getAbsolutePath()); - } - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(File, String) - */ - public FileWrapper(File parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given pathname string into an abstract - * pathname. - * @param osPathname the OS pathname - * - * @see File#File(String) - */ - public FileWrapper(String osPathname) { - super(osPathname); - } - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(String, String) - */ - public FileWrapper(String parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given <code>file:</code> URI into an - * abstract pathname. - * @param uri An absolute, hierarchical URI with a scheme equal to "file", a non-empty path - * component, and undefined authority, query, and fragment components - * - * @see File#File(URI) - */ - public FileWrapper(URI uri) { - super(uri); - } - - @Override - public InputStream getContents() throws StreamException { - try { - return new FileInputStream(this); - } catch (FileNotFoundException e) { - throw new StreamException(e, this, StreamException.Error.FILENOTFOUND); - } - } - - @Override - public void setContents(InputStream source) throws StreamException { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(this); - - byte[] buffer = new byte[1024]; - int count = 0; - while ((count = source.read(buffer)) != -1) { - fos.write(buffer, 0, count); - } - } catch (IOException e) { - throw new StreamException(e, this); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - throw new StreamException(e, this); - } - } - } - } - - @Override - public OutputStream getOutputStream() throws StreamException { - try { - return new FileOutputStream(this); - } catch (FileNotFoundException e) { - throw new StreamException(e, this); - } - } - - @Override - public PreferredWriteMode getPreferredWriteMode() { - return PreferredWriteMode.OUTPUTSTREAM; - } - - @Override - public String getOsLocation() { - return getAbsolutePath(); - } - - @Override - public boolean exists() { - return isFile(); - } - - @Override - public long getModificationStamp() { - return lastModified(); - } - - @Override - public IAbstractFolder getParentFolder() { - String p = this.getParent(); - if (p == null) { - return null; - } - return new FolderWrapper(p); - } -} diff --git a/common/src/main/java/com/android/io/FolderWrapper.java b/common/src/main/java/com/android/io/FolderWrapper.java deleted file mode 100644 index c29c934..0000000 --- a/common/src/main/java/com/android/io/FolderWrapper.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2008 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.io; - - -import java.io.File; -import java.net.URI; -import java.util.ArrayList; - -/** - * An implementation of {@link IAbstractFolder} extending {@link File}. - */ -public class FolderWrapper extends File implements IAbstractFolder { - - private static final long serialVersionUID = 1L; - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(File, String) - */ - public FolderWrapper(File parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given pathname string into an abstract - * pathname. - * @param pathname the pathname - * - * @see File#File(String) - */ - public FolderWrapper(String pathname) { - super(pathname); - } - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(String, String) - */ - public FolderWrapper(String parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given <code>file:</code> URI into an - * abstract pathname. - * @param uri An absolute, hierarchical URI with a scheme equal to "file", a non-empty path - * component, and undefined authority, query, and fragment components - * - * @see File#File(URI) - */ - public FolderWrapper(URI uri) { - super(uri); - } - - /** - * Creates a new File instance matching a give {@link File} object. - * @param file the file to match - */ - public FolderWrapper(File file) { - super(file.getAbsolutePath()); - } - - @Override - public IAbstractResource[] listMembers() { - File[] files = listFiles(); - final int count = files == null ? 0 : files.length; - IAbstractResource[] afiles = new IAbstractResource[count]; - - if (files != null) { - for (int i = 0 ; i < count ; i++) { - File f = files[i]; - if (f.isFile()) { - afiles[i] = new FileWrapper(f); - } else if (f.isDirectory()) { - afiles[i] = new FolderWrapper(f); - } - } - } - - return afiles; - } - - @Override - public boolean hasFile(final String name) { - String[] match = list(new FilenameFilter() { - @Override - public boolean accept(IAbstractFolder dir, String filename) { - return name.equals(filename); - } - }); - - return match.length > 0; - } - - @Override - public IAbstractFile getFile(String name) { - return new FileWrapper(this, name); - } - - @Override - public IAbstractFolder getFolder(String name) { - return new FolderWrapper(this, name); - } - - @Override - public IAbstractFolder getParentFolder() { - String p = this.getParent(); - if (p == null) { - return null; - } - return new FolderWrapper(p); - } - - @Override - public String getOsLocation() { - return getAbsolutePath(); - } - - @Override - public boolean exists() { - return isDirectory(); - } - - @Override - public String[] list(FilenameFilter filter) { - File[] files = listFiles(); - if (files != null && files.length > 0) { - ArrayList<String> list = new ArrayList<String>(); - - for (File file : files) { - if (filter.accept(this, file.getName())) { - list.add(file.getName()); - } - } - - return list.toArray(new String[list.size()]); - } - - return new String[0]; - } -} diff --git a/common/src/main/java/com/android/io/IAbstractFile.java b/common/src/main/java/com/android/io/IAbstractFile.java deleted file mode 100644 index 6dfc8d8..0000000 --- a/common/src/main/java/com/android/io/IAbstractFile.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2008 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.io; - -import java.io.InputStream; -import java.io.OutputStream; - -/** - * A file. - */ -public interface IAbstractFile extends IAbstractResource { - public static enum PreferredWriteMode { - INPUTSTREAM, OUTPUTSTREAM; - } - - /** - * Returns an {@link InputStream} object on the file content. - * @throws StreamException - */ - InputStream getContents() throws StreamException; - - /** - * Sets the content of the file. - * @param source the content - * @throws StreamException - */ - void setContents(InputStream source) throws StreamException; - - /** - * Returns an {@link OutputStream} to write into the file. - * @throws StreamException - */ - OutputStream getOutputStream() throws StreamException; - - /** - * Returns the preferred mode to write into the file. - */ - PreferredWriteMode getPreferredWriteMode(); - - /** - * Returns the last modification timestamp - */ - long getModificationStamp(); -} diff --git a/common/src/main/java/com/android/io/IAbstractFolder.java b/common/src/main/java/com/android/io/IAbstractFolder.java deleted file mode 100644 index 8335ef9..0000000 --- a/common/src/main/java/com/android/io/IAbstractFolder.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2008 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.io; - -import java.io.File; - -/** - * A folder. - */ -public interface IAbstractFolder extends IAbstractResource { - /** - * Instances of classes that implement this interface are used to - * filter filenames. - */ - public interface FilenameFilter { - /** - * Tests if a specified file should be included in a file list. - * - * @param dir the directory in which the file was found. - * @param name the name of the file. - * @return <code>true</code> if and only if the name should be - * included in the file list; <code>false</code> otherwise. - */ - boolean accept(IAbstractFolder dir, String name); - } - - /** - * Returns true if the receiver contains a file with a given name - * @param name the name of the file. This is the name without the path leading to the - * parent folder. - */ - boolean hasFile(String name); - - /** - * Returns an {@link IAbstractFile} representing a child of the current folder with the - * given name. The file may not actually exist. - * @param name the name of the file. - */ - IAbstractFile getFile(String name); - - /** - * Returns an {@link IAbstractFolder} representing a child of the current folder with the - * given name. The folder may not actually exist. - * @param name the name of the folder. - */ - IAbstractFolder getFolder(String name); - - /** - * Returns a list of all existing file and directory members in this folder. - * The returned array can be empty but is never null. - */ - IAbstractResource[] listMembers(); - - /** - * Returns a list of all existing file and directory members in this folder - * that satisfy the specified filter. - * - * @param filter A filename filter instance. Must not be null. - * @return An array of file names (generated using {@link File#getName()}). - * The array can be empty but is never null. - */ - String[] list(FilenameFilter filter); -} diff --git a/common/src/main/java/com/android/io/IAbstractResource.java b/common/src/main/java/com/android/io/IAbstractResource.java deleted file mode 100644 index e6358ec..0000000 --- a/common/src/main/java/com/android/io/IAbstractResource.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2008 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.io; - -/** - * Base representation of a file system resource.<p/> - * This somewhat limited interface is designed to let classes use file-system resources, without - * having the manually handle either the standard Java file or the Eclipse file API.. - */ -public interface IAbstractResource { - - /** - * Returns the name of the resource. - */ - String getName(); - - /** - * Returns the OS path of the folder location. - */ - String getOsLocation(); - - /** - * Returns whether the resource actually exists. - */ - boolean exists(); - - /** - * Returns the parent folder or null if there is no parent. - */ - IAbstractFolder getParentFolder(); - - /** - * Deletes the resource. - */ - boolean delete(); -} diff --git a/common/src/main/java/com/android/io/StreamException.java b/common/src/main/java/com/android/io/StreamException.java deleted file mode 100644 index 9f632f4..0000000 --- a/common/src/main/java/com/android/io/StreamException.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2010 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.io; - - -/** - * Exception thrown when {@link IAbstractFile#getContents()} fails. - */ -public class StreamException extends Exception { - private static final long serialVersionUID = 1L; - - public static enum Error { - DEFAULT, OUTOFSYNC, FILENOTFOUND; - } - - private final Error mError; - private final IAbstractFile mFile; - - public StreamException(Exception e, IAbstractFile file) { - this(e, file, Error.DEFAULT); - } - - public StreamException(Exception e, IAbstractFile file, Error error) { - super(e); - mFile = file; - mError = error; - } - - public Error getError() { - return mError; - } - - public IAbstractFile getFile() { - return mFile; - } -} diff --git a/common/src/main/java/com/android/prefs/AndroidLocation.java b/common/src/main/java/com/android/prefs/AndroidLocation.java deleted file mode 100644 index 6af8e9b..0000000 --- a/common/src/main/java/com/android/prefs/AndroidLocation.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2008 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.prefs; - -import com.android.annotations.NonNull; - -import java.io.File; - -/** - * Manages the location of the android files (including emulator files, ddms config, debug keystore) - */ -public final class AndroidLocation { - - /** - * The name of the .android folder returned by {@link #getFolder()}. - */ - public static final String FOLDER_DOT_ANDROID = ".android"; - - /** - * Virtual Device folder inside the path returned by {@link #getFolder()} - */ - public static final String FOLDER_AVD = "avd"; - - /** - * Throw when the location of the android folder couldn't be found. - */ - public static final class AndroidLocationException extends Exception { - private static final long serialVersionUID = 1L; - - public AndroidLocationException(String string) { - super(string); - } - } - - private static String sPrefsLocation = null; - - /** - * Returns the folder used to store android related files. - * @return an OS specific path, terminated by a separator. - * @throws AndroidLocationException - */ - @NonNull public final static String getFolder() throws AndroidLocationException { - if (sPrefsLocation == null) { - String home = findValidPath("ANDROID_SDK_HOME", "user.home", "HOME"); - - // if the above failed, we throw an exception. - if (home == null) { - throw new AndroidLocationException( - "Unable to get the Android SDK home directory.\n" + - "Make sure the environment variable ANDROID_SDK_HOME is set up."); - } else { - sPrefsLocation = home; - if (!sPrefsLocation.endsWith(File.separator)) { - sPrefsLocation += File.separator; - } - sPrefsLocation += FOLDER_DOT_ANDROID + File.separator; - } - } - - // make sure the folder exists! - File f = new File(sPrefsLocation); - if (f.exists() == false) { - try { - f.mkdir(); - } catch (SecurityException e) { - AndroidLocationException e2 = new AndroidLocationException(String.format( - "Unable to create folder '%1$s'. " + - "This is the path of preference folder expected by the Android tools.", - sPrefsLocation)); - e2.initCause(e); - throw e2; - } - } else if (f.isFile()) { - throw new AndroidLocationException(sPrefsLocation + - " is not a directory! " + - "This is the path of preference folder expected by the Android tools."); - } - - return sPrefsLocation; - } - - /** - * Resets the folder used to store android related files. For testing. - */ - public final static void resetFolder() { - sPrefsLocation = null; - } - - /** - * Checks a list of system properties and/or system environment variables for validity, and - * existing director, and returns the first one. - * @param names - * @return the content of the first property/variable. - */ - private static String findValidPath(String... names) { - for (String name : names) { - String path; - if (name.indexOf('.') != -1) { - path = System.getProperty(name); - } else { - path = System.getenv(name); - } - - if (path != null) { - File f = new File(path); - if (f.isDirectory()) { - return path; - } - } - } - - return null; - } -} diff --git a/common/src/main/java/com/android/utils/ILogger.java b/common/src/main/java/com/android/utils/ILogger.java deleted file mode 100644 index 7df5d10..0000000 --- a/common/src/main/java/com/android/utils/ILogger.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.utils; - -import com.android.annotations.NonNull; - -import java.util.Formatter; - -/** - * Interface used to display warnings/errors while parsing the SDK content. - * <p/> - * There are a few default implementations available: - * <ul> - * <li> {@link NullLogger} is an implementation that does <em>nothing</em> with the log. - * Useful for limited cases where you need to call a class that requires a non-null logging - * yet the calling code does not have any mean of reporting logs itself. It can be - * acceptable for use as a temporary implementation but most of the time that means the caller - * code needs to be reworked to take a logger object from its own caller. - * </li> - * <li> {@link StdLogger} is an implementation that dumps the log to {@link System#out} or - * {@link System#err}. This is useful for unit tests or code that does not have any GUI. - * GUI based apps based should not use it and should provide a better way to report to the user. - * </li> - * </ul> - */ -public interface ILogger { - - /** - * Prints an error message. - * - * @param t is an optional {@link Throwable} or {@link Exception}. If non-null, its - * message will be printed out. - * @param msgFormat is an optional error format. If non-null, it will be printed - * using a {@link Formatter} with the provided arguments. - * @param args provides the arguments for errorFormat. - */ - void error(Throwable t, String msgFormat, Object... args); - - /** - * Prints a warning message. - * - * @param msgFormat is a string format to be used with a {@link Formatter}. Cannot be null. - * @param args provides the arguments for warningFormat. - */ - void warning(@NonNull String msgFormat, Object... args); - - /** - * Prints an information message. - * - * @param msgFormat is a string format to be used with a {@link Formatter}. Cannot be null. - * @param args provides the arguments for msgFormat. - */ - void info(@NonNull String msgFormat, Object... args); - - /** - * Prints a verbose message. - * - * @param msgFormat is a string format to be used with a {@link Formatter}. Cannot be null. - * @param args provides the arguments for msgFormat. - */ - void verbose(@NonNull String msgFormat, Object... args); - -} diff --git a/common/src/main/java/com/android/utils/NullLogger.java b/common/src/main/java/com/android/utils/NullLogger.java deleted file mode 100644 index 8b1a3d9..0000000 --- a/common/src/main/java/com/android/utils/NullLogger.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.utils; - -import com.android.annotations.NonNull; - -/** - * Dummy implementation of an {@link ILogger}. - * <p/> - * Use {@link #getLogger()} to get a default instance of this {@link NullLogger}. - */ -public class NullLogger implements ILogger { - - private static final ILogger sThis = new NullLogger(); - - public static ILogger getLogger() { - return sThis; - } - - @Override - public void error(Throwable t, String errorFormat, Object... args) { - // ignore - } - - @Override - public void warning(@NonNull String warningFormat, Object... args) { - // ignore - } - - @Override - public void info(@NonNull String msgFormat, Object... args) { - // ignore - } - - @Override - public void verbose(@NonNull String msgFormat, Object... args) { - // ignore - } - -} diff --git a/common/src/main/java/com/android/utils/Pair.java b/common/src/main/java/com/android/utils/Pair.java deleted file mode 100644 index 63694de..0000000 --- a/common/src/main/java/com/android/utils/Pair.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2010 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.utils; - -/** - * A Pair class is simply a 2-tuple for use in this package. We might want to - * think about adding something like this to a more central utility place, or - * replace it by a common tuple class if one exists, or even rewrite the layout - * classes using this Pair by a more dedicated data structure (so we don't have - * to pass around generic signatures as is currently done, though at least the - * construction is helped a bit by the {@link #of} factory method. - * - * @param <S> The type of the first value - * @param <T> The type of the second value - */ -public class Pair<S,T> { - private final S mFirst; - private final T mSecond; - - // Use {@link Pair#of} factory instead since it infers generic types - private Pair(S first, T second) { - this.mFirst = first; - this.mSecond = second; - } - - /** - * Return the first item in the pair - * - * @return the first item in the pair - */ - public S getFirst() { - return mFirst; - } - - /** - * Return the second item in the pair - * - * @return the second item in the pair - */ - public T getSecond() { - return mSecond; - } - - /** - * Constructs a new pair of the given two objects, inferring generic types. - * - * @param first the first item to store in the pair - * @param second the second item to store in the pair - * @param <S> the type of the first item - * @param <T> the type of the second item - * @return a new pair wrapping the two items - */ - public static <S,T> Pair<S,T> of(S first, T second) { - return new Pair<S,T>(first,second); - } - - @Override - public String toString() { - return "Pair [first=" + mFirst + ", second=" + mSecond + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode()); - result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode()); - return result; - } - - @SuppressWarnings("unchecked") - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Pair other = (Pair) obj; - if (mFirst == null) { - if (other.mFirst != null) - return false; - } else if (!mFirst.equals(other.mFirst)) - return false; - if (mSecond == null) { - if (other.mSecond != null) - return false; - } else if (!mSecond.equals(other.mSecond)) - return false; - return true; - } -} diff --git a/common/src/main/java/com/android/utils/PositionXmlParser.java b/common/src/main/java/com/android/utils/PositionXmlParser.java deleted file mode 100644 index 73574d5..0000000 --- a/common/src/main/java/com/android/utils/PositionXmlParser.java +++ /dev/null @@ -1,729 +0,0 @@ -/* - * 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.utils; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.Text; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.Locator; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -/** - * A simple DOM XML parser which can retrieve exact beginning and end offsets - * (and line and column numbers) for element nodes as well as attribute nodes. - */ -public class PositionXmlParser { - private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$ - private static final String UTF_16 = "UTF_16"; //$NON-NLS-1$ - private static final String UTF_16LE = "UTF_16LE"; //$NON-NLS-1$ - private static final String CONTENT_KEY = "contents"; //$NON-NLS-1$ - private final static String POS_KEY = "offsets"; //$NON-NLS-1$ - private static final String NAMESPACE_PREFIX_FEATURE = - "http://xml.org/sax/features/namespace-prefixes"; //$NON-NLS-1$ - private static final String NAMESPACE_FEATURE = - "http://xml.org/sax/features/namespaces"; //$NON-NLS-1$ - /** See http://www.w3.org/TR/REC-xml/#NT-EncodingDecl */ - private static final Pattern ENCODING_PATTERN = - Pattern.compile("encoding=['\"](\\S*)['\"]");//$NON-NLS-1$ - - /** - * Parses the XML content from the given input stream. - * - * @param input the input stream containing the XML to be parsed - * @return the corresponding document - * @throws ParserConfigurationException if a SAX parser is not available - * @throws SAXException if the document contains a parsing error - * @throws IOException if something is seriously wrong. This should not - * happen since the input source is known to be constructed from - * a string. - */ - @Nullable - public Document parse(@NonNull InputStream input) - throws ParserConfigurationException, SAXException, IOException { - // Read in all the data - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buf = new byte[1024]; - while (true) { - int r = input.read(buf); - if (r == -1) { - break; - } - out.write(buf, 0, r); - } - input.close(); - return parse(out.toByteArray()); - } - - /** - * Parses the XML content from the given byte array - * - * @param data the raw XML data (with unknown encoding) - * @return the corresponding document - * @throws ParserConfigurationException if a SAX parser is not available - * @throws SAXException if the document contains a parsing error - * @throws IOException if something is seriously wrong. This should not - * happen since the input source is known to be constructed from - * a string. - */ - @Nullable - public Document parse(@NonNull byte[] data) - throws ParserConfigurationException, SAXException, IOException { - String xml = getXmlString(data); - return parse(xml, new InputSource(new StringReader(xml)), true); - } - - /** - * Parses the given XML content. - * - * @param xml the XML string to be parsed. This must be in the correct - * encoding already. - * @return the corresponding document - * @throws ParserConfigurationException if a SAX parser is not available - * @throws SAXException if the document contains a parsing error - * @throws IOException if something is seriously wrong. This should not - * happen since the input source is known to be constructed from - * a string. - */ - @Nullable - public Document parse(@NonNull String xml) - throws ParserConfigurationException, SAXException, IOException { - return parse(xml, new InputSource(new StringReader(xml)), true); - } - - @NonNull - private Document parse(@NonNull String xml, @NonNull InputSource input, boolean checkBom) - throws ParserConfigurationException, SAXException, IOException { - try { - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setFeature(NAMESPACE_FEATURE, true); - factory.setFeature(NAMESPACE_PREFIX_FEATURE, true); - SAXParser parser = factory.newSAXParser(); - DomBuilder handler = new DomBuilder(xml); - parser.parse(input, handler); - return handler.getDocument(); - } catch (SAXException e) { - if (checkBom && e.getMessage().contains("Content is not allowed in prolog")) { - // Byte order mark in the string? Skip it. There are many markers - // (see http://en.wikipedia.org/wiki/Byte_order_mark) so here we'll - // just skip those up to the XML prolog beginning character, < - xml = xml.replaceFirst("^([\\W]+)<","<"); //$NON-NLS-1$ //$NON-NLS-2$ - return parse(xml, new InputSource(new StringReader(xml)), false); - } - throw e; - } - } - - /** - * Returns the String corresponding to the given byte array of XML data - * (with unknown encoding). This method attempts to guess the encoding based - * on the XML prologue. - * @param data the XML data to be decoded into a string - * @return a string corresponding to the XML data - */ - public static String getXmlString(byte[] data) { - int offset = 0; - - String defaultCharset = UTF_8; - String charset = null; - // Look for the byte order mark, to see if we need to remove bytes from - // the input stream (and to determine whether files are big endian or little endian) etc - // for files which do not specify the encoding. - // See http://unicode.org/faq/utf_bom.html#BOM for more. - if (data.length > 4) { - if (data[0] == (byte)0xef && data[1] == (byte)0xbb && data[2] == (byte)0xbf) { - // UTF-8 - defaultCharset = charset = UTF_8; - offset += 3; - } else if (data[0] == (byte)0xfe && data[1] == (byte)0xff) { - // UTF-16, big-endian - defaultCharset = charset = UTF_16; - offset += 2; - } else if (data[0] == (byte)0x0 && data[1] == (byte)0x0 - && data[2] == (byte)0xfe && data[3] == (byte)0xff) { - // UTF-32, big-endian - defaultCharset = charset = "UTF_32"; //$NON-NLS-1$ - offset += 4; - } else if (data[0] == (byte)0xff && data[1] == (byte)0xfe - && data[2] == (byte)0x0 && data[3] == (byte)0x0) { - // UTF-32, little-endian. We must check for this *before* looking for - // UTF_16LE since UTF_32LE has the same prefix! - defaultCharset = charset = "UTF_32LE"; //$NON-NLS-1$ - offset += 4; - } else if (data[0] == (byte)0xff && data[1] == (byte)0xfe) { - // UTF-16, little-endian - defaultCharset = charset = UTF_16LE; - offset += 2; - } - } - int length = data.length - offset; - - // Guess encoding by searching for an encoding= entry in the first line. - // The prologue, and the encoding names, will always be in ASCII - which means - // we don't need to worry about strange character encodings for the prologue characters. - // However, one wrinkle is that the whole file may be encoded in something like UTF-16 - // where there are two bytes per character, so we can't just look for - // ['e','n','c','o','d','i','n','g'] etc in the byte array since there could be - // multiple bytes for each character. However, since again the prologue is in ASCII, - // we can just drop the zeroes. - boolean seenOddZero = false; - boolean seenEvenZero = false; - int prologueStart = -1; - for (int lineEnd = offset; lineEnd < data.length; lineEnd++) { - if (data[lineEnd] == 0) { - if ((lineEnd - offset) % 2 == 0) { - seenEvenZero = true; - } else { - seenOddZero = true; - } - } else if (data[lineEnd] == '\n' || data[lineEnd] == '\r') { - break; - } else if (data[lineEnd] == '<') { - prologueStart = lineEnd; - } else if (data[lineEnd] == '>') { - // End of prologue. Quick check to see if this is a utf-8 file since that's - // common - for (int i = lineEnd - 4; i >= 0; i--) { - if ((data[i] == 'u' || data[i] == 'U') - && (data[i + 1] == 't' || data[i + 1] == 'T') - && (data[i + 2] == 'f' || data[i + 2] == 'F') - && (data[i + 3] == '-' || data[i + 3] == '_') - && (data[i + 4] == '8') - ) { - charset = UTF_8; - break; - } - } - - if (charset == null) { - StringBuilder sb = new StringBuilder(); - for (int i = prologueStart; i <= lineEnd; i++) { - if (data[i] != 0) { - sb.append((char) data[i]); - } - } - String prologue = sb.toString(); - int encodingIndex = prologue.indexOf("encoding"); //$NON-NLS-1$ - if (encodingIndex != -1) { - Matcher matcher = ENCODING_PATTERN.matcher(prologue); - if (matcher.find(encodingIndex)) { - charset = matcher.group(1); - } - } - } - - break; - } - } - - // No prologue on the first line, and no byte order mark: Assume UTF-8/16 - if (charset == null) { - charset = seenOddZero ? UTF_16LE : seenEvenZero ? UTF_16 : UTF_8; - } - - String xml = null; - try { - xml = new String(data, offset, length, charset); - } catch (UnsupportedEncodingException e) { - try { - if (charset != defaultCharset) { - xml = new String(data, offset, length, defaultCharset); - } - } catch (UnsupportedEncodingException u) { - // Just use the default encoding below - } - } - if (xml == null) { - xml = new String(data, offset, length); - } - return xml; - } - - /** - * Returns the position for the given node. This is the start position. The - * end position can be obtained via {@link Position#getEnd()}. - * - * @param node the node to look up position for - * @return the position, or null if the node type is not supported for - * position info - */ - @Nullable - public Position getPosition(@NonNull Node node) { - return getPosition(node, -1, -1); - } - - /** - * Returns the position for the given node. This is the start position. The - * end position can be obtained via {@link Position#getEnd()}. A specific - * range within the node can be specified with the {@code start} and - * {@code end} parameters. - * - * @param node the node to look up position for - * @param start the relative offset within the node range to use as the - * starting position, inclusive, or -1 to not limit the range - * @param end the relative offset within the node range to use as the ending - * position, or -1 to not limit the range - * @return the position, or null if the node type is not supported for - * position info - */ - @Nullable - public Position getPosition(@NonNull Node node, int start, int end) { - // Look up the position information stored while parsing for the given node. - // Note however that we only store position information for elements (because - // there is no SAX callback for individual attributes). - // Therefore, this method special cases this: - // -- First, it looks at the owner element and uses its position - // information as a first approximation. - // -- Second, it uses that, as well as the original XML text, to search - // within the node range for an exact text match on the attribute name - // and if found uses that as the exact node offsets instead. - if (node instanceof Attr) { - Attr attr = (Attr) node; - Position pos = (Position) attr.getOwnerElement().getUserData(POS_KEY); - if (pos != null) { - int startOffset = pos.getOffset(); - int endOffset = pos.getEnd().getOffset(); - if (start != -1) { - startOffset += start; - if (end != -1) { - endOffset = start + end; - } - } - - // Find attribute in the text - String contents = (String) node.getOwnerDocument().getUserData(CONTENT_KEY); - if (contents == null) { - return null; - } - - // Locate the name=value attribute in the source text - // Fast string check first for the common occurrence - String name = attr.getName(); - Pattern pattern = Pattern.compile( - String.format("%1$s\\s*=\\s*[\"'].*[\"']", name)); //$NON-NLS-1$ - Matcher matcher = pattern.matcher(contents); - if (matcher.find(startOffset) && matcher.start() <= endOffset) { - int index = matcher.start(); - // Adjust the line and column to this new offset - int line = pos.getLine(); - int column = pos.getColumn(); - for (int offset = pos.getOffset(); offset < index; offset++) { - char t = contents.charAt(offset); - if (t == '\n') { - line++; - column = 0; - } else { - column++; - } - } - - Position attributePosition = createPosition(line, column, index); - // Also set end range for retrieval in getLocation - attributePosition.setEnd(createPosition(line, column + matcher.end() - index, - matcher.end())); - return attributePosition; - } else { - // No regexp match either: just fall back to element position - return pos; - } - } - } else if (node instanceof Text) { - // Position of parent element, if any - Position pos = null; - if (node.getPreviousSibling() != null) { - pos = (Position) node.getPreviousSibling().getUserData(POS_KEY); - } - if (pos == null) { - pos = (Position) node.getParentNode().getUserData(POS_KEY); - } - if (pos != null) { - // Attempt to point forward to the actual text node - int startOffset = pos.getOffset(); - int endOffset = pos.getEnd().getOffset(); - int line = pos.getLine(); - int column = pos.getColumn(); - - // Find attribute in the text - String contents = (String) node.getOwnerDocument().getUserData(CONTENT_KEY); - if (contents == null || contents.length() < endOffset) { - return null; - } - - boolean inAttribute = false; - for (int offset = startOffset; offset <= endOffset; offset++) { - char c = contents.charAt(offset); - if (c == '>' && !inAttribute) { - // Found the end of the element open tag: this is where the - // text begins. - - // Skip > - offset++; - column++; - - String text = node.getNodeValue(); - int textIndex = 0; - int textLength = text.length(); - int newLine = line; - int newColumn = column; - if (start != -1) { - textLength = Math.min(textLength, start); - for (; textIndex < textLength; textIndex++) { - char t = text.charAt(textIndex); - if (t == '\n') { - newLine++; - newColumn = 0; - } else { - newColumn++; - } - } - } else { - // Skip text whitespace prefix, if the text node contains - // non-whitespace characters - for (; textIndex < textLength; textIndex++) { - char t = text.charAt(textIndex); - if (t == '\n') { - newLine++; - newColumn = 0; - } else if (!Character.isWhitespace(t)) { - break; - } else { - newColumn++; - } - } - } - if (textIndex == text.length()) { - textIndex = 0; // Whitespace node - } else { - line = newLine; - column = newColumn; - } - - Position attributePosition = createPosition(line, column, - offset + textIndex); - // Also set end range for retrieval in getLocation - if (end != -1) { - attributePosition.setEnd(createPosition(line, column, - offset + end)); - } else { - attributePosition.setEnd(createPosition(line, column, - offset + textLength)); - } - return attributePosition; - } else if (c == '"') { - inAttribute = !inAttribute; - } else if (c == '\n') { - line++; - column = -1; // pre-subtract column added below - } - column++; - } - - return pos; - } - } - - return (Position) node.getUserData(POS_KEY); - } - - /** - * SAX parser handler which incrementally builds up a DOM document as we go - * along, and updates position information along the way. Position - * information is attached to the DOM nodes by setting user data with the - * {@link POS_KEY} key. - */ - private final class DomBuilder extends DefaultHandler { - private final String mXml; - private final Document mDocument; - private Locator mLocator; - private int mCurrentLine = 0; - private int mCurrentOffset; - private int mCurrentColumn; - private final List<Element> mStack = new ArrayList<Element>(); - private final StringBuilder mPendingText = new StringBuilder(); - - private DomBuilder(String xml) throws ParserConfigurationException { - mXml = xml; - - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - factory.setValidating(false); - DocumentBuilder docBuilder = factory.newDocumentBuilder(); - mDocument = docBuilder.newDocument(); - mDocument.setUserData(CONTENT_KEY, xml, null); - } - - /** Returns the document parsed by the handler */ - Document getDocument() { - return mDocument; - } - - @Override - public void setDocumentLocator(Locator locator) { - this.mLocator = locator; - } - - @Override - public void startElement(String uri, String localName, String qName, - Attributes attributes) throws SAXException { - try { - flushText(); - Element element = mDocument.createElement(qName); - for (int i = 0; i < attributes.getLength(); i++) { - if (attributes.getURI(i) != null && attributes.getURI(i).length() > 0) { - Attr attr = mDocument.createAttributeNS(attributes.getURI(i), - attributes.getQName(i)); - attr.setValue(attributes.getValue(i)); - element.setAttributeNodeNS(attr); - assert attr.getOwnerElement() == element; - } else { - Attr attr = mDocument.createAttribute(attributes.getQName(i)); - attr.setValue(attributes.getValue(i)); - element.setAttributeNode(attr); - assert attr.getOwnerElement() == element; - } - } - - Position pos = getCurrentPosition(); - - // The starting position reported to us by SAX is really the END of the - // open tag in an element, when all the attributes have been processed. - // We have to scan backwards to find the real beginning. We'll do that - // by scanning backwards. - // -1: Make sure that when we have <foo></foo> we don't consider </foo> - // the beginning since pos.offset will typically point to the first character - // AFTER the element open tag, which could be a closing tag or a child open - // tag - - for (int offset = pos.getOffset() - 1; offset >= 0; offset--) { - char c = mXml.charAt(offset); - // < cannot appear in attribute values or anywhere else within - // an element open tag, so we know the first occurrence is the real - // element start - if (c == '<') { - // Adjust line position - int line = pos.getLine(); - for (int i = offset, n = pos.getOffset(); i < n; i++) { - if (mXml.charAt(i) == '\n') { - line--; - } - } - - // Compute new column position - int column = 0; - for (int i = offset - 1; i >= 0; i--, column++) { - if (mXml.charAt(i) == '\n') { - break; - } - } - - pos = createPosition(line, column, offset); - break; - } - } - - element.setUserData(POS_KEY, pos, null); - mStack.add(element); - } catch (Exception t) { - throw new SAXException(t); - } - } - - @Override - public void endElement(String uri, String localName, String qName) { - flushText(); - Element element = mStack.remove(mStack.size() - 1); - - Position pos = (Position) element.getUserData(POS_KEY); - assert pos != null; - pos.setEnd(getCurrentPosition()); - - if (mStack.isEmpty()) { - mDocument.appendChild(element); - } else { - Element parent = mStack.get(mStack.size() - 1); - parent.appendChild(element); - } - } - - /** - * Returns a position holder for the current position. The most - * important part of this function is to incrementally compute the - * offset as well, by counting forwards until it reaches the new line - * number and column position of the XML parser, counting characters as - * it goes along. - */ - private Position getCurrentPosition() { - int line = mLocator.getLineNumber() - 1; - int column = mLocator.getColumnNumber() - 1; - - // Compute offset incrementally now that we have the new line and column - // numbers - int xmlLength = mXml.length(); - while (mCurrentLine < line && mCurrentOffset < xmlLength) { - char c = mXml.charAt(mCurrentOffset); - if (c == '\r' && mCurrentOffset < xmlLength - 1) { - if (mXml.charAt(mCurrentOffset + 1) != '\n') { - mCurrentLine++; - mCurrentColumn = 0; - } - } else if (c == '\n') { - mCurrentLine++; - mCurrentColumn = 0; - } else { - mCurrentColumn++; - } - mCurrentOffset++; - } - - mCurrentOffset += column - mCurrentColumn; - if (mCurrentOffset >= xmlLength) { - // The parser sometimes passes wrong column numbers at the - // end of the file: Ensure that the offset remains valid. - mCurrentOffset = xmlLength; - } - mCurrentColumn = column; - - return createPosition(mCurrentLine, mCurrentColumn, mCurrentOffset); - } - - @Override - public void characters(char c[], int start, int length) throws SAXException { - mPendingText.append(c, start, length); - } - - private void flushText() { - if (mPendingText.length() > 0 && !mStack.isEmpty()) { - Element element = mStack.get(mStack.size() - 1); - Node textNode = mDocument.createTextNode(mPendingText.toString()); - element.appendChild(textNode); - mPendingText.setLength(0); - } - } - } - - /** - * Creates a position while constructing the DOM document. This method - * allows a subclass to create a custom implementation of the position - * class. - * - * @param line the line number for the position - * @param column the column number for the position - * @param offset the character offset - * @return a new position - */ - @NonNull - protected Position createPosition(int line, int column, int offset) { - return new DefaultPosition(line, column, offset); - } - - protected interface Position { - /** - * Linked position: for a begin position this will point to the - * corresponding end position. For an end position this will be null. - * - * @return the end position, or null - */ - @Nullable - public Position getEnd(); - - /** - * Linked position: for a begin position this will point to the - * corresponding end position. For an end position this will be null. - * - * @param end the end position - */ - public void setEnd(@NonNull Position end); - - /** @return the line number, 0-based */ - public int getLine(); - - /** @return the offset number, 0-based */ - public int getOffset(); - - /** @return the column number, 0-based, and -1 if the column number if not known */ - public int getColumn(); - } - - protected static class DefaultPosition implements Position { - /** The line number (0-based where the first line is line 0) */ - private final int mLine; - private final int mColumn; - private final int mOffset; - private Position mEnd; - - /** - * Creates a new {@link Position} - * - * @param line the 0-based line number, or -1 if unknown - * @param column the 0-based column number, or -1 if unknown - * @param offset the offset, or -1 if unknown - */ - public DefaultPosition(int line, int column, int offset) { - this.mLine = line; - this.mColumn = column; - this.mOffset = offset; - } - - @Override - public int getLine() { - return mLine; - } - - @Override - public int getOffset() { - return mOffset; - } - - @Override - public int getColumn() { - return mColumn; - } - - @Override - public Position getEnd() { - return mEnd; - } - - @Override - public void setEnd(@NonNull Position end) { - mEnd = end; - } - } -} diff --git a/common/src/main/java/com/android/utils/SdkUtils.java b/common/src/main/java/com/android/utils/SdkUtils.java deleted file mode 100644 index 160f95d..0000000 --- a/common/src/main/java/com/android/utils/SdkUtils.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * 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.utils; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; - -/** Miscellaneous utilities used by the Android SDK tools */ -public class SdkUtils { - /** - * Returns true if the given string ends with the given suffix, using a - * case-insensitive comparison. - * - * @param string the full string to be checked - * @param suffix the suffix to be checked for - * @return true if the string case-insensitively ends with the given suffix - */ - public static boolean endsWithIgnoreCase(String string, String suffix) { - return string.regionMatches(true /* ignoreCase */, string.length() - suffix.length(), - suffix, 0, suffix.length()); - } - - /** - * Returns true if the given sequence ends with the given suffix (case - * sensitive). - * - * @param sequence the character sequence to be checked - * @param suffix the suffix to look for - * @return true if the given sequence ends with the given suffix - */ - public static boolean endsWith(CharSequence sequence, CharSequence suffix) { - return endsWith(sequence, sequence.length(), suffix); - } - - /** - * Returns true if the given sequence ends at the given offset with the given suffix (case - * sensitive) - * - * @param sequence the character sequence to be checked - * @param endOffset the offset at which the sequence is considered to end - * @param suffix the suffix to look for - * @return true if the given sequence ends with the given suffix - */ - public static boolean endsWith(CharSequence sequence, int endOffset, CharSequence suffix) { - if (endOffset < suffix.length()) { - return false; - } - - for (int i = endOffset - 1, j = suffix.length() - 1; j >= 0; i--, j--) { - if (sequence.charAt(i) != suffix.charAt(j)) { - return false; - } - } - - return true; - } - - /** - * Returns true if the given string starts with the given prefix, using a - * case-insensitive comparison. - * - * @param string the full string to be checked - * @param prefix the prefix to be checked for - * @return true if the string case-insensitively starts with the given prefix - */ - public static boolean startsWithIgnoreCase(String string, String prefix) { - return string.regionMatches(true /* ignoreCase */, 0, prefix, 0, prefix.length()); - } - - /** - * Returns true if the given string starts at the given offset with the - * given prefix, case insensitively. - * - * @param string the full string to be checked - * @param offset the offset in the string to start looking - * @param prefix the prefix to be checked for - * @return true if the string case-insensitively starts at the given offset - * with the given prefix - */ - public static boolean startsWith(String string, int offset, String prefix) { - return string.regionMatches(true /* ignoreCase */, offset, prefix, 0, prefix.length()); - } - - /** - * Strips the whitespace from the given string - * - * @param string the string to be cleaned up - * @return the string, without whitespace - */ - public static String stripWhitespace(String string) { - StringBuilder sb = new StringBuilder(string.length()); - for (int i = 0, n = string.length(); i < n; i++) { - char c = string.charAt(i); - if (!Character.isWhitespace(c)) { - sb.append(c); - } - } - - return sb.toString(); - } - - /** - * Returns true if the given string has an upper case character. - * - * @param s the string to check - * @return true if it contains uppercase characters - */ - public static boolean hasUpperCaseCharacter(String s) { - for (int i = 0; i < s.length(); i++) { - if (Character.isUpperCase(s.charAt(i))) { - return true; - } - } - - return false; - } - - /** For use by {@link #getLineSeparator()} */ - private static String sLineSeparator; - - /** - * Returns the default line separator to use. - * <p> - * NOTE: If you have an associated IDocument (Eclipse), it is better to call - * TextUtilities#getDefaultLineDelimiter(IDocument) since that will - * allow (for example) editing a \r\n-delimited document on a \n-delimited - * platform and keep a consistent usage of delimiters in the file. - * - * @return the delimiter string to use - */ - @NonNull - public static String getLineSeparator() { - if (sLineSeparator == null) { - // This is guaranteed to exist: - sLineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$ - } - - return sLineSeparator; - } - - /** - * Wraps the given text at the given line width, with an optional hanging - * indent. - * - * @param text the text to be wrapped - * @param lineWidth the number of characters to wrap the text to - * @param hangingIndent the hanging indent (to be used for the second and - * subsequent lines in each paragraph, or null if not known - * @return the string, wrapped - */ - @NonNull - public static String wrap( - @NonNull String text, - int lineWidth, - @Nullable String hangingIndent) { - if (hangingIndent == null) { - hangingIndent = ""; - } - int explanationLength = text.length(); - StringBuilder sb = new StringBuilder(explanationLength * 2); - int index = 0; - - while (index < explanationLength) { - int lineEnd = text.indexOf('\n', index); - int next; - - if (lineEnd != -1 && (lineEnd - index) < lineWidth) { - next = lineEnd + 1; - } else { - // Line is longer than available width; grab as much as we can - lineEnd = Math.min(index + lineWidth, explanationLength); - if (lineEnd - index < lineWidth) { - next = explanationLength; - } else { - // then back up to the last space - int lastSpace = text.lastIndexOf(' ', lineEnd); - if (lastSpace > index) { - lineEnd = lastSpace; - next = lastSpace + 1; - } else { - // No space anywhere on the line: it contains something wider than - // can fit (like a long URL) so just hard break it - next = lineEnd + 1; - } - } - } - - if (sb.length() > 0) { - sb.append(hangingIndent); - } else { - lineWidth -= hangingIndent.length(); - } - - sb.append(text.substring(index, lineEnd)); - sb.append('\n'); - index = next; - } - - return sb.toString(); - } - -} diff --git a/common/src/main/java/com/android/utils/StdLogger.java b/common/src/main/java/com/android/utils/StdLogger.java deleted file mode 100644 index 05eb456..0000000 --- a/common/src/main/java/com/android/utils/StdLogger.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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.utils; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; - -import java.io.PrintStream; -import java.util.Formatter; - - -/** - * An implementation of {@link ILogger} that prints to {@link System#out} and {@link System#err}. - * <p/> - * - */ -public class StdLogger implements ILogger { - - private final Level mLevel; - - public enum Level { - VERBOSE(0), - INFO(1), - WARNING(2), - ERROR(3); - - private final int mLevel; - - Level(int level) { - mLevel = level; - } - } - - /** - * Creates the {@link StdLogger} with a given log {@link Level}. - * @param level the log Level. - */ - public StdLogger(@NonNull Level level) { - if (level == null) { - throw new IllegalArgumentException("level cannot be null"); - } - - mLevel = level; - } - - /** - * Returns the logger's log {@link Level}. - * @return the log level. - */ - public Level getLevel() { - return mLevel; - } - - /** - * Prints an error message. - * <p/> - * The message will be tagged with "Error" on the output so the caller does not - * need to put such a prefix in the format string. - * <p/> - * The output is done on {@link System#err}. - * <p/> - * This is always displayed, independent of the logging {@link Level}. - * - * @param t is an optional {@link Throwable} or {@link Exception}. If non-null, it's - * message will be printed out. - * @param errorFormat is an optional error format. If non-null, it will be printed - * using a {@link Formatter} with the provided arguments. - * @param args provides the arguments for errorFormat. - */ - @Override - public void error(Throwable t, String errorFormat, Object... args) { - if (errorFormat != null) { - String msg = String.format("Error: " + errorFormat, args); - - printMessage(msg, System.err); - } - if (t != null) { - System.err.println(String.format("Error: %1$s", t.getMessage())); - } - } - - /** - * Prints a warning message. - * <p/> - * The message will be tagged with "Warning" on the output so the caller does not - * need to put such a prefix in the format string. - * <p/> - * The output is done on {@link System#out}. - * <p/> - * This is displayed only if the logging {@link Level} is {@link Level#WARNING} or higher. - * - * @param warningFormat is a string format to be used with a {@link Formatter}. Cannot be null. - * @param args provides the arguments for warningFormat. - */ - @Override - public void warning(@NonNull String warningFormat, Object... args) { - if (mLevel.mLevel > Level.WARNING.mLevel) { - return; - } - - String msg = String.format("Warning: " + warningFormat, args); - - printMessage(msg, System.out); - } - - /** - * Prints an info message. - * <p/> - * The output is done on {@link System#out}. - * <p/> - * This is displayed only if the logging {@link Level} is {@link Level#INFO} or higher. - * - * @param msgFormat is a string format to be used with a {@link Formatter}. Cannot be null. - * @param args provides the arguments for msgFormat. - */ - @Override - public void info(@NonNull String msgFormat, Object... args) { - if (mLevel.mLevel > Level.INFO.mLevel) { - return; - } - - String msg = String.format(msgFormat, args); - - printMessage(msg, System.out); - } - - /** - * Prints a verbose message. - * <p/> - * The output is done on {@link System#out}. - * <p/> - * This is displayed only if the logging {@link Level} is {@link Level#VERBOSE} or higher. - * - * @param msgFormat is a string format to be used with a {@link Formatter}. Cannot be null. - * @param args provides the arguments for msgFormat. - */ - @Override - public void verbose(@NonNull String msgFormat, Object... args) { - if (mLevel.mLevel > Level.VERBOSE.mLevel) { - return; - } - - String msg = String.format(msgFormat, args); - - printMessage(msg, System.out); - } - - private void printMessage(String msg, PrintStream stream) { - if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS && - !msg.endsWith("\r\n") && - msg.endsWith("\n")) { - // remove last \n so that println can use \r\n as needed. - msg = msg.substring(0, msg.length() - 1); - } - - stream.print(msg); - - if (!msg.endsWith("\n")) { - stream.println(); - } - } - -} diff --git a/common/src/main/java/com/android/utils/XmlUtils.java b/common/src/main/java/com/android/utils/XmlUtils.java deleted file mode 100644 index c77baee..0000000 --- a/common/src/main/java/com/android/utils/XmlUtils.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * 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.utils; - -import static com.android.SdkConstants.AMP_ENTITY; -import static com.android.SdkConstants.ANDROID_NS_NAME; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.APOS_ENTITY; -import static com.android.SdkConstants.APP_PREFIX; -import static com.android.SdkConstants.LT_ENTITY; -import static com.android.SdkConstants.QUOT_ENTITY; -import static com.android.SdkConstants.XMLNS; -import static com.android.SdkConstants.XMLNS_PREFIX; -import static com.android.SdkConstants.XMLNS_URI; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.google.common.base.Splitter; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.HashSet; - -/** XML Utilities */ -public class XmlUtils { - /** - * Returns the namespace prefix matching the requested namespace URI. - * If no such declaration is found, returns the default "android" prefix for - * the Android URI, and "app" for other URI's. By default the app namespace - * will be created. If this is not desirable, call - * {@link #lookupNamespacePrefix(Node, String, boolean)} instead. - * - * @param node The current node. Must not be null. - * @param nsUri The namespace URI of which the prefix is to be found, - * e.g. {@link SdkConstants#ANDROID_URI} - * @return The first prefix declared or the default "android" prefix - * (or "app" for non-Android URIs) - */ - @NonNull - public static String lookupNamespacePrefix(@NonNull Node node, @NonNull String nsUri) { - String defaultPrefix = ANDROID_URI.equals(nsUri) ? ANDROID_NS_NAME : APP_PREFIX; - return lookupNamespacePrefix(node, nsUri, defaultPrefix, true /*create*/); - } - - /** - * Returns the namespace prefix matching the requested namespace URI. If no - * such declaration is found, returns the default "android" prefix for the - * Android URI, and "app" for other URI's. - * - * @param node The current node. Must not be null. - * @param nsUri The namespace URI of which the prefix is to be found, e.g. - * {@link SdkConstants#ANDROID_URI} - * @param create whether the namespace declaration should be created, if - * necessary - * @return The first prefix declared or the default "android" prefix (or - * "app" for non-Android URIs) - */ - @NonNull - public static String lookupNamespacePrefix(@NonNull Node node, @NonNull String nsUri, - boolean create) { - String defaultPrefix = ANDROID_URI.equals(nsUri) ? ANDROID_NS_NAME : APP_PREFIX; - return lookupNamespacePrefix(node, nsUri, defaultPrefix, create); - } - - /** - * Returns the namespace prefix matching the requested namespace URI. If no - * such declaration is found, returns the default "android" prefix. - * - * @param node The current node. Must not be null. - * @param nsUri The namespace URI of which the prefix is to be found, e.g. - * {@link SdkConstants#ANDROID_URI} - * @param defaultPrefix The default prefix (root) to use if the namespace is - * not found. If null, do not create a new namespace if this URI - * is not defined for the document. - * @param create whether the namespace declaration should be created, if - * necessary - * @return The first prefix declared or the provided prefix (possibly with a - * number appended to avoid conflicts with existing prefixes. - */ - public static String lookupNamespacePrefix( - @Nullable Node node, @Nullable String nsUri, @Nullable String defaultPrefix, - boolean create) { - // Note: Node.lookupPrefix is not implemented in wst/xml/core NodeImpl.java - // The following code emulates this simple call: - // String prefix = node.lookupPrefix(NS_RESOURCES); - - // if the requested URI is null, it denotes an attribute with no namespace. - if (nsUri == null) { - return null; - } - - // per XML specification, the "xmlns" URI is reserved - if (XMLNS_URI.equals(nsUri)) { - return XMLNS; - } - - HashSet<String> visited = new HashSet<String>(); - Document doc = node == null ? null : node.getOwnerDocument(); - - // Ask the document about it. This method may not be implemented by the Document. - String nsPrefix = null; - try { - nsPrefix = doc != null ? doc.lookupPrefix(nsUri) : null; - if (nsPrefix != null) { - return nsPrefix; - } - } catch (Throwable t) { - // ignore - } - - // If that failed, try to look it up manually. - // This also gathers prefixed in use in the case we want to generate a new one below. - for (; node != null && node.getNodeType() == Node.ELEMENT_NODE; - node = node.getParentNode()) { - NamedNodeMap attrs = node.getAttributes(); - for (int n = attrs.getLength() - 1; n >= 0; --n) { - Node attr = attrs.item(n); - if (XMLNS.equals(attr.getPrefix())) { - String uri = attr.getNodeValue(); - nsPrefix = attr.getLocalName(); - // Is this the URI we are looking for? If yes, we found its prefix. - if (nsUri.equals(uri)) { - return nsPrefix; - } - visited.add(nsPrefix); - } - } - } - - // Failed the find a prefix. Generate a new sensible default prefix, unless - // defaultPrefix was null in which case the caller does not want the document - // modified. - if (defaultPrefix == null) { - return null; - } - - // - // We need to make sure the prefix is not one that was declared in the scope - // visited above. Pick a unique prefix from the provided default prefix. - String prefix = defaultPrefix; - String base = prefix; - for (int i = 1; visited.contains(prefix); i++) { - prefix = base + Integer.toString(i); - } - // Also create & define this prefix/URI in the XML document as an attribute in the - // first element of the document. - if (doc != null) { - node = doc.getFirstChild(); - while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { - node = node.getNextSibling(); - } - if (node != null && create) { - // This doesn't work: - //Attr attr = doc.createAttributeNS(XMLNS_URI, prefix); - //attr.setPrefix(XMLNS); - // - // Xerces throws - //org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or - // change an object in a way which is incorrect with regard to namespaces. - // - // Instead pass in the concatenated prefix. (This is covered by - // the UiElementNodeTest#testCreateNameSpace() test.) - Attr attr = doc.createAttributeNS(XMLNS_URI, XMLNS_PREFIX + prefix); - attr.setValue(nsUri); - node.getAttributes().setNamedItemNS(attr); - } - } - - return prefix; - } - - /** - * Converts the given attribute value to an XML-attribute-safe value, meaning that - * single and double quotes are replaced with their corresponding XML entities. - * - * @param attrValue the value to be escaped - * @return the escaped value - */ - @NonNull - public static String toXmlAttributeValue(@NonNull String attrValue) { - for (int i = 0, n = attrValue.length(); i < n; i++) { - char c = attrValue.charAt(i); - if (c == '"' || c == '\'' || c == '<' || c == '&') { - StringBuilder sb = new StringBuilder(2 * attrValue.length()); - appendXmlAttributeValue(sb, attrValue); - return sb.toString(); - } - } - - return attrValue; - } - - /** - * Converts the given attribute value to an XML-text-safe value, meaning that - * less than and ampersand characters are escaped. - * - * @param textValue the text value to be escaped - * @return the escaped value - */ - @NonNull - public static String toXmlTextValue(@NonNull String textValue) { - for (int i = 0, n = textValue.length(); i < n; i++) { - char c = textValue.charAt(i); - if (c == '<' || c == '&') { - StringBuilder sb = new StringBuilder(2 * textValue.length()); - appendXmlTextValue(sb, textValue); - return sb.toString(); - } - } - - return textValue; - } - - /** - * Appends text to the given {@link StringBuilder} and escapes it as required for a - * DOM attribute node. - * - * @param sb the string builder - * @param attrValue the attribute value to be appended and escaped - */ - public static void appendXmlAttributeValue(@NonNull StringBuilder sb, - @NonNull String attrValue) { - int n = attrValue.length(); - // &, ", ' and < are illegal in attributes; see http://www.w3.org/TR/REC-xml/#NT-AttValue - // (' legal in a " string and " is legal in a ' string but here we'll stay on the safe - // side) - for (int i = 0; i < n; i++) { - char c = attrValue.charAt(i); - if (c == '"') { - sb.append(QUOT_ENTITY); - } else if (c == '<') { - sb.append(LT_ENTITY); - } else if (c == '\'') { - sb.append(APOS_ENTITY); - } else if (c == '&') { - sb.append(AMP_ENTITY); - } else { - sb.append(c); - } - } - } - - /** - * Appends text to the given {@link StringBuilder} and escapes it as required for a - * DOM text node. - * - * @param sb the string builder - * @param textValue the text value to be appended and escaped - */ - public static void appendXmlTextValue(@NonNull StringBuilder sb, @NonNull String textValue) { - for (int i = 0, n = textValue.length(); i < n; i++) { - char c = textValue.charAt(i); - if (c == '<') { - sb.append(LT_ENTITY); - } else if (c == '&') { - sb.append(AMP_ENTITY); - } else { - sb.append(c); - } - } - } - - /** - * Dump an XML tree to string. This isn't going to do a beautiful job pretty - * printing the XML; it's intended mostly for non-user editable files and - * for debugging. If true, preserve whitespace exactly as in the DOM - * document (typically used for a DOM which is already formatted), otherwise - * this method will insert some newlines here and there (for example, one - * per element and one per attribute.) - * - * @param node the node (which can be a document, an element, a text node, - * etc. - * @param preserveWhitespace whether to preserve the whitespace (text nodes) - * in the DOM - * @return a string version of the file - */ - public static String toXml(Node node, boolean preserveWhitespace) { - StringBuilder sb = new StringBuilder(1000); - - append(sb, node, 0, preserveWhitespace); - - return sb.toString(); - } - - private static void indent(StringBuilder sb, int indent) { - for (int i = 0; i < indent; i++) { - sb.append(" "); - } - } - - private static void append( - @NonNull StringBuilder sb, - @NonNull Node node, - int indent, - boolean preserveWhitespace) { - short nodeType = node.getNodeType(); - switch (nodeType) { - case Node.DOCUMENT_NODE: - case Node.DOCUMENT_FRAGMENT_NODE: { - sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); //$NON-NLS-1$ - NodeList children = node.getChildNodes(); - for (int i = 0, n = children.getLength(); i < n; i++) { - append(sb, children.item(i), indent, preserveWhitespace); - } - break; - } - case Node.COMMENT_NODE: - case Node.TEXT_NODE: { - if (nodeType == Node.COMMENT_NODE) { - if (!preserveWhitespace) { - indent(sb, indent); - } - sb.append("<!--"); //$NON-NLS-1$ - if (!preserveWhitespace) { - sb.append('\n'); - } - } - String text = node.getNodeValue(); - if (!preserveWhitespace) { - text = text.trim(); - for (String line : Splitter.on('\n').split(text)) { - indent(sb, indent + 1); - sb.append(toXmlTextValue(line)); - sb.append('\n'); - } - } else { - sb.append(toXmlTextValue(text)); - } - if (nodeType == Node.COMMENT_NODE) { - if (!preserveWhitespace) { - indent(sb, indent); - } - sb.append("-->"); //$NON-NLS-1$ - if (!preserveWhitespace) { - sb.append('\n'); - } - } - break; - } - case Node.ELEMENT_NODE: { - if (!preserveWhitespace) { - indent(sb, indent); - } - sb.append('<'); - Element element = (Element) node; - sb.append(element.getTagName()); - - NamedNodeMap attributes = element.getAttributes(); - NodeList children = element.getChildNodes(); - int childCount = children.getLength(); - int attributeCount = attributes.getLength(); - - if (attributeCount > 0) { - for (int i = 0; i < attributeCount; i++) { - Node attribute = attributes.item(i); - sb.append(' '); - sb.append(attribute.getNodeName()); - sb.append('=').append('"'); - sb.append(toXmlAttributeValue(attribute.getNodeValue())); - sb.append('"'); - } - } - - if (childCount == 0) { - sb.append('/'); - } - sb.append('>'); - if (!preserveWhitespace) { - sb.append('\n'); - } - if (childCount > 0) { - for (int i = 0; i < childCount; i++) { - Node child = children.item(i); - append(sb, child, indent + 1, preserveWhitespace); - } - if (!preserveWhitespace) { - if (sb.charAt(sb.length() - 1) != '\n') { - sb.append('\n'); - } - indent(sb, indent); - } - sb.append('<').append('/'); - sb.append(element.getTagName()); - sb.append('>'); - if (!preserveWhitespace) { - sb.append('\n'); - } - } - break; - } - - default: - throw new UnsupportedOperationException( - "Unsupported node type " + nodeType + ": not yet implemented"); - } - } -}
\ No newline at end of file diff --git a/common/src/main/java/com/android/xml/AndroidManifest.java b/common/src/main/java/com/android/xml/AndroidManifest.java deleted file mode 100644 index 306471e..0000000 --- a/common/src/main/java/com/android/xml/AndroidManifest.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright (C) 2009 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.xml; - -import com.android.SdkConstants; -import com.android.io.IAbstractFile; -import com.android.io.IAbstractFolder; -import com.android.io.StreamException; - -import org.w3c.dom.Node; -import org.xml.sax.InputSource; - -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpressionException; - -/** - * Helper and Constants for the AndroidManifest.xml file. - * - */ -public final class AndroidManifest { - - public static final String NODE_MANIFEST = "manifest"; - public static final String NODE_APPLICATION = "application"; - public static final String NODE_ACTIVITY = "activity"; - public static final String NODE_ACTIVITY_ALIAS = "activity-alias"; - public static final String NODE_SERVICE = "service"; - public static final String NODE_RECEIVER = "receiver"; - public static final String NODE_PROVIDER = "provider"; - public static final String NODE_INTENT = "intent-filter"; - public static final String NODE_ACTION = "action"; - public static final String NODE_CATEGORY = "category"; - public static final String NODE_USES_SDK = "uses-sdk"; - public static final String NODE_INSTRUMENTATION = "instrumentation"; - public static final String NODE_USES_LIBRARY = "uses-library"; - public static final String NODE_SUPPORTS_SCREENS = "supports-screens"; - public static final String NODE_USES_CONFIGURATION = "uses-configuration"; - public static final String NODE_USES_FEATURE = "uses-feature"; - - public static final String ATTRIBUTE_PACKAGE = "package"; - public static final String ATTRIBUTE_VERSIONCODE = "versionCode"; - public static final String ATTRIBUTE_NAME = "name"; - public static final String ATTRIBUTE_REQUIRED = "required"; - public static final String ATTRIBUTE_GLESVERSION = "glEsVersion"; - public static final String ATTRIBUTE_PROCESS = "process"; - public static final String ATTRIBUTE_DEBUGGABLE = "debuggable"; - public static final String ATTRIBUTE_LABEL = "label"; - public static final String ATTRIBUTE_ICON = "icon"; - public static final String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; - public static final String ATTRIBUTE_TARGET_SDK_VERSION = "targetSdkVersion"; - public static final String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; - public static final String ATTRIBUTE_TARGET_ACTIVITY = "targetActivity"; - public static final String ATTRIBUTE_MANAGE_SPACE_ACTIVITY = "manageSpaceActivity"; - public static final String ATTRIBUTE_EXPORTED = "exported"; - public static final String ATTRIBUTE_RESIZEABLE = "resizeable"; - public static final String ATTRIBUTE_ANYDENSITY = "anyDensity"; - public static final String ATTRIBUTE_SMALLSCREENS = "smallScreens"; - public static final String ATTRIBUTE_NORMALSCREENS = "normalScreens"; - public static final String ATTRIBUTE_LARGESCREENS = "largeScreens"; - public static final String ATTRIBUTE_REQ_5WAYNAV = "reqFiveWayNav"; - public static final String ATTRIBUTE_REQ_NAVIGATION = "reqNavigation"; - public static final String ATTRIBUTE_REQ_HARDKEYBOARD = "reqHardKeyboard"; - public static final String ATTRIBUTE_REQ_KEYBOARDTYPE = "reqKeyboardType"; - public static final String ATTRIBUTE_REQ_TOUCHSCREEN = "reqTouchScreen"; - public static final String ATTRIBUTE_THEME = "theme"; - public static final String ATTRIBUTE_BACKUP_AGENT = "backupAgent"; - public static final String ATTRIBUTE_PARENT_ACTIVITY_NAME = "parentActivityName"; - - /** - * Returns an {@link IAbstractFile} object representing the manifest for the given project. - * - * @param projectFolder The project containing the manifest file. - * @return An IAbstractFile object pointing to the manifest or null if the manifest - * is missing. - */ - public static IAbstractFile getManifest(IAbstractFolder projectFolder) { - IAbstractFile file = projectFolder.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML); - if (file.exists()) { - return file; - } - - return null; - } - - /** - * Returns the package for a given project. - * @param projectFolder the folder of the project. - * @return the package info or null (or empty) if not found. - * @throws XPathExpressionException - * @throws StreamException If any error happens when reading the manifest. - */ - public static String getPackage(IAbstractFolder projectFolder) - throws XPathExpressionException, StreamException { - IAbstractFile file = getManifest(projectFolder); - if (file != null) { - return getPackage(file); - } - - return null; - } - - /** - * Returns the package for a given manifest. - * @param manifestFile the manifest to parse. - * @return the package info or null (or empty) if not found. - * @throws XPathExpressionException - * @throws StreamException If any error happens when reading the manifest. - */ - public static String getPackage(IAbstractFile manifestFile) - throws XPathExpressionException, StreamException { - XPath xPath = AndroidXPathFactory.newXPath(); - - return xPath.evaluate( - "/" + NODE_MANIFEST + - "/@" + ATTRIBUTE_PACKAGE, - new InputSource(manifestFile.getContents())); - } - - /** - * Returns whether the manifest is set to make the application debuggable. - * - * If the give manifest does not contain the debuggable attribute then the application - * is considered to not be debuggable. - * - * @param manifestFile the manifest to parse. - * @return true if the application is debuggable. - * @throws XPathExpressionException - * @throws StreamException If any error happens when reading the manifest. - */ - public static boolean getDebuggable(IAbstractFile manifestFile) - throws XPathExpressionException, StreamException { - XPath xPath = AndroidXPathFactory.newXPath(); - - String value = xPath.evaluate( - "/" + NODE_MANIFEST + - "/" + NODE_APPLICATION + - "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + - ":" + ATTRIBUTE_DEBUGGABLE, - new InputSource(manifestFile.getContents())); - - // default is not debuggable, which is the same behavior as parseBoolean - return Boolean.parseBoolean(value); - } - - /** - * Returns the value of the versionCode attribute or -1 if the value is not set. - * @param manifestFile the manifest file to read the attribute from. - * @return the integer value or -1 if not set. - * @throws XPathExpressionException - * @throws StreamException If any error happens when reading the manifest. - */ - public static int getVersionCode(IAbstractFile manifestFile) - throws XPathExpressionException, StreamException { - XPath xPath = AndroidXPathFactory.newXPath(); - - String result = xPath.evaluate( - "/" + NODE_MANIFEST + - "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + - ":" + ATTRIBUTE_VERSIONCODE, - new InputSource(manifestFile.getContents())); - - try { - return Integer.parseInt(result); - } catch (NumberFormatException e) { - return -1; - } - } - - /** - * Returns whether the version Code attribute is set in a given manifest. - * @param manifestFile the manifest to check - * @return true if the versionCode attribute is present and its value is not empty. - * @throws XPathExpressionException - * @throws StreamException If any error happens when reading the manifest. - */ - public static boolean hasVersionCode(IAbstractFile manifestFile) - throws XPathExpressionException, StreamException { - XPath xPath = AndroidXPathFactory.newXPath(); - - Object result = xPath.evaluate( - "/" + NODE_MANIFEST + - "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + - ":" + ATTRIBUTE_VERSIONCODE, - new InputSource(manifestFile.getContents()), - XPathConstants.NODE); - - if (result != null) { - Node node = (Node)result; - if (node.getNodeValue().length() > 0) { - return true; - } - } - - return false; - } - - /** - * Returns the value of the minSdkVersion attribute. - * <p/> - * If the attribute is set with an int value, the method returns an Integer object. - * <p/> - * If the attribute is set with a codename, it returns the codename as a String object. - * <p/> - * If the attribute is not set, it returns null. - * - * @param manifestFile the manifest file to read the attribute from. - * @return the attribute value. - * @throws XPathExpressionException - * @throws StreamException If any error happens when reading the manifest. - */ - public static Object getMinSdkVersion(IAbstractFile manifestFile) - throws XPathExpressionException, StreamException { - XPath xPath = AndroidXPathFactory.newXPath(); - - String result = xPath.evaluate( - "/" + NODE_MANIFEST + - "/" + NODE_USES_SDK + - "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + - ":" + ATTRIBUTE_MIN_SDK_VERSION, - new InputSource(manifestFile.getContents())); - - try { - return Integer.valueOf(result); - } catch (NumberFormatException e) { - return result.length() > 0 ? result : null; - } - } - - /** - * Returns the value of the targetSdkVersion attribute (defaults to 1 if the attribute is - * not set), or -1 if the value is a codename. - * @param manifestFile the manifest file to read the attribute from. - * @return the integer value or -1 if not set. - * @throws XPathExpressionException - * @throws StreamException If any error happens when reading the manifest. - */ - public static Integer getTargetSdkVersion(IAbstractFile manifestFile) - throws XPathExpressionException, StreamException { - XPath xPath = AndroidXPathFactory.newXPath(); - - String result = xPath.evaluate( - "/" + NODE_MANIFEST + - "/" + NODE_USES_SDK + - "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + - ":" + ATTRIBUTE_TARGET_SDK_VERSION, - new InputSource(manifestFile.getContents())); - - try { - return Integer.valueOf(result); - } catch (NumberFormatException e) { - return result.length() > 0 ? -1 : null; - } - } - - /** - * Returns the application icon for a given manifest. - * @param manifestFile the manifest to parse. - * @return the icon or null (or empty) if not found. - * @throws XPathExpressionException - * @throws StreamException If any error happens when reading the manifest. - */ - public static String getApplicationIcon(IAbstractFile manifestFile) - throws XPathExpressionException, StreamException { - XPath xPath = AndroidXPathFactory.newXPath(); - - return xPath.evaluate( - "/" + NODE_MANIFEST + - "/" + NODE_APPLICATION + - "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + - ":" + ATTRIBUTE_ICON, - new InputSource(manifestFile.getContents())); - } - - /** - * Returns the application label for a given manifest. - * @param manifestFile the manifest to parse. - * @return the label or null (or empty) if not found. - * @throws XPathExpressionException - * @throws StreamException If any error happens when reading the manifest. - */ - public static String getApplicationLabel(IAbstractFile manifestFile) - throws XPathExpressionException, StreamException { - XPath xPath = AndroidXPathFactory.newXPath(); - - return xPath.evaluate( - "/" + NODE_MANIFEST + - "/" + NODE_APPLICATION + - "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + - ":" + ATTRIBUTE_LABEL, - new InputSource(manifestFile.getContents())); - } - - /** - * Combines a java package, with a class value from the manifest to make a fully qualified - * class name - * @param javaPackage the java package from the manifest. - * @param className the class name from the manifest. - * @return the fully qualified class name. - */ - public static String combinePackageAndClassName(String javaPackage, String className) { - if (className == null || className.length() == 0) { - return javaPackage; - } - if (javaPackage == null || javaPackage.length() == 0) { - return className; - } - - // the class name can be a subpackage (starts with a '.' - // char), a simple class name (no dot), or a full java package - boolean startWithDot = (className.charAt(0) == '.'); - boolean hasDot = (className.indexOf('.') != -1); - if (startWithDot || hasDot == false) { - - // add the concatenation of the package and class name - if (startWithDot) { - return javaPackage + className; - } else { - return javaPackage + '.' + className; - } - } else { - // just add the class as it should be a fully qualified java name. - return className; - } - } - - /** - * Given a fully qualified activity name (e.g. com.foo.test.MyClass) and given a project - * package base name (e.g. com.foo), returns the relative activity name that would be used - * the "name" attribute of an "activity" element. - * - * @param fullActivityName a fully qualified activity class name, e.g. "com.foo.test.MyClass" - * @param packageName The project base package name, e.g. "com.foo" - * @return The relative activity name if it can be computed or the original fullActivityName. - */ - public static String extractActivityName(String fullActivityName, String packageName) { - if (packageName != null && fullActivityName != null) { - if (packageName.length() > 0 && fullActivityName.startsWith(packageName)) { - String name = fullActivityName.substring(packageName.length()); - if (name.length() > 0 && name.charAt(0) == '.') { - return name; - } - } - } - - return fullActivityName; - } -} diff --git a/common/src/main/java/com/android/xml/AndroidXPathFactory.java b/common/src/main/java/com/android/xml/AndroidXPathFactory.java deleted file mode 100644 index ee5b87b..0000000 --- a/common/src/main/java/com/android/xml/AndroidXPathFactory.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2009 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.xml; - -import com.android.SdkConstants; - -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import javax.xml.XMLConstants; -import javax.xml.namespace.NamespaceContext; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathFactory; - -/** - * XPath factory with automatic support for the android name space. - */ -public class AndroidXPathFactory { - /** Default prefix for android name space: 'android' */ - public final static String DEFAULT_NS_PREFIX = "android"; //$NON-NLS-1$ - - private final static XPathFactory sFactory = XPathFactory.newInstance(); - - /** Name space context for Android resource XML files. */ - private static class AndroidNamespaceContext implements NamespaceContext { - private final static AndroidNamespaceContext sThis = new AndroidNamespaceContext( - DEFAULT_NS_PREFIX); - - private final String mAndroidPrefix; - private final List<String> mAndroidPrefixes; - - /** - * Returns the default {@link AndroidNamespaceContext}. - */ - private static AndroidNamespaceContext getDefault() { - return sThis; - } - - /** - * Construct the context with the prefix associated with the android namespace. - * @param androidPrefix the Prefix - */ - public AndroidNamespaceContext(String androidPrefix) { - mAndroidPrefix = androidPrefix; - mAndroidPrefixes = Collections.singletonList(mAndroidPrefix); - } - - @Override - public String getNamespaceURI(String prefix) { - if (prefix != null) { - if (prefix.equals(mAndroidPrefix)) { - return SdkConstants.NS_RESOURCES; - } - } - - return XMLConstants.NULL_NS_URI; - } - - @Override - public String getPrefix(String namespaceURI) { - if (SdkConstants.NS_RESOURCES.equals(namespaceURI)) { - return mAndroidPrefix; - } - - return null; - } - - @Override - public Iterator<?> getPrefixes(String namespaceURI) { - if (SdkConstants.NS_RESOURCES.equals(namespaceURI)) { - return mAndroidPrefixes.iterator(); - } - - return null; - } - } - - /** - * Creates a new XPath object, specifying which prefix in the query is used for the - * android namespace. - * @param androidPrefix The namespace prefix. - */ - public static XPath newXPath(String androidPrefix) { - XPath xpath = sFactory.newXPath(); - xpath.setNamespaceContext(new AndroidNamespaceContext(androidPrefix)); - return xpath; - } - - /** - * Creates a new XPath object using the default prefix for the android namespace. - * @see #DEFAULT_NS_PREFIX - */ - public static XPath newXPath() { - XPath xpath = sFactory.newXPath(); - xpath.setNamespaceContext(AndroidNamespaceContext.getDefault()); - return xpath; - } -} diff --git a/common/src/test/.classpath b/common/src/test/.classpath deleted file mode 100644 index 7564f2f..0000000 --- a/common/src/test/.classpath +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="java"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> - <classpathentry combineaccessrules="false" kind="src" path="/common"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-13.0.1.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/guava-tools/src.zip"/> - <classpathentry kind="output" path="bin"/> -</classpath> diff --git a/common/src/test/.project b/common/src/test/.project deleted file mode 100644 index 9f550a3..0000000 --- a/common/src/test/.project +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>common-tests</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> diff --git a/common/src/test/.settings/org.moreunit.prefs b/common/src/test/.settings/org.moreunit.prefs deleted file mode 100644 index 5c0456e..0000000 --- a/common/src/test/.settings/org.moreunit.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -org.moreunit.prefixes= -org.moreunit.unitsourcefolder=common-tests\:java\:common\:src/main/java -org.moreunit.useprojectsettings=true diff --git a/common/src/test/java/com/android/utils/PositionXmlParserTest.java b/common/src/test/java/com/android/utils/PositionXmlParserTest.java deleted file mode 100644 index 18eda43..0000000 --- a/common/src/test/java/com/android/utils/PositionXmlParserTest.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * 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.utils; - -import com.android.utils.PositionXmlParser.Position; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.w3c.dom.Text; - -import java.io.BufferedOutputStream; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.OutputStreamWriter; -import java.io.Writer; - -import junit.framework.TestCase; - -@SuppressWarnings("javadoc") -public class PositionXmlParserTest extends TestCase { - public void test() throws Exception { - String xml = - "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + - "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + - " android:layout_width=\"match_parent\"\n" + - " android:layout_height=\"wrap_content\"\n" + - " android:orientation=\"vertical\" >\n" + - "\n" + - " <Button\n" + - " android:id=\"@+id/button1\"\n" + - " android:layout_width=\"wrap_content\"\n" + - " android:layout_height=\"wrap_content\"\n" + - " android:text=\"Button\" />\n" + - "\n" + - " <Button\n" + - " android:id=\"@+id/button2\"\n" + - " android:layout_width=\"wrap_content\"\n" + - " android:layout_height=\"wrap_content\"\n" + - " android:text=\"Button\" />\n" + - "\n" + - "</LinearLayout>\n"; - PositionXmlParser parser = new PositionXmlParser(); - File file = File.createTempFile("parsertest", ".xml"); - Writer fw = new BufferedWriter(new FileWriter(file)); - fw.write(xml); - fw.close(); - Document document = parser.parse(new FileInputStream(file)); - assertNotNull(document); - - // Basic parsing heart beat tests - Element linearLayout = (Element) document.getElementsByTagName("LinearLayout").item(0); - assertNotNull(linearLayout); - NodeList buttons = document.getElementsByTagName("Button"); - assertEquals(2, buttons.getLength()); - final String ANDROID_URI = "http://schemas.android.com/apk/res/android"; - assertEquals("wrap_content", - linearLayout.getAttributeNS(ANDROID_URI, "layout_height")); - - // Check attribute positions - Attr attr = linearLayout.getAttributeNodeNS(ANDROID_URI, "layout_width"); - assertNotNull(attr); - Position start = parser.getPosition(attr); - Position end = start.getEnd(); - assertEquals(2, start.getLine()); - assertEquals(4, start.getColumn()); - assertEquals(xml.indexOf("android:layout_width"), start.getOffset()); - assertEquals(2, end.getLine()); - String target = "android:layout_width=\"match_parent\""; - assertEquals(xml.indexOf(target) + target.length(), end.getOffset()); - - // Check element positions - Element button = (Element) buttons.item(0); - start = parser.getPosition(button); - end = start.getEnd(); - assertNull(end.getEnd()); - assertEquals(6, start.getLine()); - assertEquals(4, start.getColumn()); - assertEquals(xml.indexOf("<Button"), start.getOffset()); - assertEquals(xml.indexOf("/>") + 2, end.getOffset()); - assertEquals(10, end.getLine()); - int button1End = end.getOffset(); - - Element button2 = (Element) buttons.item(1); - start = parser.getPosition(button2); - end = start.getEnd(); - assertEquals(12, start.getLine()); - assertEquals(4, start.getColumn()); - assertEquals(xml.indexOf("<Button", button1End), start.getOffset()); - assertEquals(xml.indexOf("/>", start.getOffset()) + 2, end.getOffset()); - assertEquals(16, end.getLine()); - - file.delete(); - } - - public void testText() throws Exception { - String xml = - "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + - "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + - " android:layout_width=\"match_parent\"\n" + - " android:layout_height=\"wrap_content\"\n" + - " android:orientation=\"vertical\" >\n" + - "\n" + - " <Button\n" + - " android:id=\"@+id/button1\"\n" + - " android:layout_width=\"wrap_content\"\n" + - " android:layout_height=\"wrap_content\"\n" + - " android:text=\"Button\" />\n" + - " some text\n" + - "\n" + - "</LinearLayout>\n"; - PositionXmlParser parser = new PositionXmlParser(); - File file = File.createTempFile("parsertest", ".xml"); - file.deleteOnExit(); - Writer fw = new BufferedWriter(new FileWriter(file)); - fw.write(xml); - fw.close(); - Document document = parser.parse(new FileInputStream(file)); - assertNotNull(document); - - // Basic parsing heart beat tests - Element linearLayout = (Element) document.getElementsByTagName("LinearLayout").item(0); - assertNotNull(linearLayout); - NodeList buttons = document.getElementsByTagName("Button"); - assertEquals(1, buttons.getLength()); - final String ANDROID_URI = "http://schemas.android.com/apk/res/android"; - assertEquals("wrap_content", - linearLayout.getAttributeNS(ANDROID_URI, "layout_height")); - - // Check text positions - Element button = (Element) buttons.item(0); - Text text = (Text) button.getNextSibling(); - assertNotNull(text); - - // Check attribute positions - Position start = parser.getPosition(text); - assertEquals(11, start.getLine()); - assertEquals(10, start.getColumn()); - assertEquals(xml.indexOf("some text"), start.getOffset()); - - // Check attribute positions with text node offsets - start = parser.getPosition(text, 13, 15); - assertEquals(11, start.getLine()); - assertEquals(12, start.getColumn()); - assertEquals(xml.indexOf("me"), start.getOffset()); - } - - public void testLineEndings() throws Exception { - // Test for http://code.google.com/p/android/issues/detail?id=22925 - String xml = - "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + - "<LinearLayout>\r\n" + - "\r" + - "<LinearLayout></LinearLayout>\r\n" + - "</LinearLayout>\r\n"; - PositionXmlParser parser = new PositionXmlParser(); - File file = File.createTempFile("parsertest2", ".xml"); - Writer fw = new BufferedWriter(new FileWriter(file)); - fw.write(xml); - fw.close(); - Document document = parser.parse(new FileInputStream(file)); - assertNotNull(document); - - file.delete(); - } - - private static void checkEncoding(String encoding, boolean writeBom, boolean writeEncoding, - String lineEnding) - throws Exception { - // Norwegian extra vowel characters such as "latin small letter a with ring above" - String value = "\u00e6\u00d8\u00e5"; - StringBuilder sb = new StringBuilder(); - - sb.append("<?xml version=\"1.0\""); - if (writeEncoding) { - sb.append(" encoding=\""); - sb.append(encoding); - sb.append("\""); - } - sb.append("?>"); - sb.append(lineEnding); - sb.append( - "<!-- This is a " + lineEnding + - " multiline comment" + lineEnding + - "-->" + lineEnding + - "<foo "); - int startAttrOffset = sb.length(); - sb.append("attr=\""); - sb.append(value); - sb.append("\""); - sb.append(">" + lineEnding + - lineEnding + - "<bar></bar>" + lineEnding + - "</foo>" + lineEnding); - PositionXmlParser parser = new PositionXmlParser(); - File file = File.createTempFile("parsertest" + encoding + writeBom + writeEncoding, - ".xml"); - BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file)); - OutputStreamWriter writer = new OutputStreamWriter(stream, encoding); - - if (writeBom) { - String normalized = encoding.toLowerCase().replace("-", "_"); - if (normalized.equals("utf_8")) { - stream.write(0xef); - stream.write(0xbb); - stream.write(0xbf); - } else if (normalized.equals("utf_16")) { - stream.write(0xfe); - stream.write(0xff); - } else if (normalized.equals("utf_16le")) { - stream.write(0xff); - stream.write(0xfe); - } else if (normalized.equals("utf_32")) { - stream.write(0x0); - stream.write(0x0); - stream.write(0xfe); - stream.write(0xff); - } else if (normalized.equals("utf_32le")) { - stream.write(0xff); - stream.write(0xfe); - stream.write(0x0); - stream.write(0x0); - } else { - fail("Can't write BOM for encoding " + encoding); - } - } - - writer.write(sb.toString()); - writer.close(); - - Document document = parser.parse(new FileInputStream(file)); - assertNotNull(document); - Element root = document.getDocumentElement(); - assertEquals(file.getPath(), value, root.getAttribute("attr")); - assertEquals(4, parser.getPosition(root).getLine()); - - Attr attribute = root.getAttributeNode("attr"); - assertNotNull(attribute); - Position position = parser.getPosition(attribute); - assertNotNull(position); - assertEquals(4, position.getLine()); - assertEquals(startAttrOffset, position.getOffset()); - - file.delete(); - } - - public void testEncoding() throws Exception { - checkEncoding("utf-8", false /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF-8", false /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF_16", false /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF-16", false /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF_16LE", false /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF_32", false /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF_32LE", false /*bom*/, true /*encoding*/, "\n"); - checkEncoding("windows-1252", false /*bom*/, true /*encoding*/, "\n"); - checkEncoding("MacRoman", false /*bom*/, true /*encoding*/, "\n"); - checkEncoding("ISO-8859-1", false /*bom*/, true /*encoding*/, "\n"); - checkEncoding("iso-8859-1", false /*bom*/, true /*encoding*/, "\n"); - - // Try BOM's (with no encoding specified) - checkEncoding("utf-8", true /*bom*/, false /*encoding*/, "\n"); - checkEncoding("UTF-8", true /*bom*/, false /*encoding*/, "\n"); - checkEncoding("UTF_16", true /*bom*/, false /*encoding*/, "\n"); - checkEncoding("UTF-16", true /*bom*/, false /*encoding*/, "\n"); - checkEncoding("UTF_16LE", true /*bom*/, false /*encoding*/, "\n"); - checkEncoding("UTF_32", true /*bom*/, false /*encoding*/, "\n"); - checkEncoding("UTF_32LE", true /*bom*/, false /*encoding*/, "\n"); - - // Try default encodings (only defined for utf-8 and utf-16) - checkEncoding("utf-8", false /*bom*/, false /*encoding*/, "\n"); - checkEncoding("UTF-8", false /*bom*/, false /*encoding*/, "\n"); - checkEncoding("UTF_16", false /*bom*/, false /*encoding*/, "\n"); - checkEncoding("UTF-16", false /*bom*/, false /*encoding*/, "\n"); - checkEncoding("UTF_16LE", false /*bom*/, false /*encoding*/, "\n"); - - // Try BOM's (with explicit encoding specified) - checkEncoding("utf-8", true /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF-8", true /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF_16", true /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF-16", true /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF_16LE", true /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF_32", true /*bom*/, true /*encoding*/, "\n"); - checkEncoding("UTF_32LE", true /*bom*/, true /*encoding*/, "\n"); - - // Make sure this works for \r and \r\n as well - checkEncoding("UTF-16", false /*bom*/, true /*encoding*/, "\r"); - checkEncoding("UTF_16LE", false /*bom*/, true /*encoding*/, "\r"); - checkEncoding("UTF_32", false /*bom*/, true /*encoding*/, "\r"); - checkEncoding("UTF_32LE", false /*bom*/, true /*encoding*/, "\r"); - checkEncoding("windows-1252", false /*bom*/, true /*encoding*/, "\r"); - checkEncoding("MacRoman", false /*bom*/, true /*encoding*/, "\r"); - checkEncoding("ISO-8859-1", false /*bom*/, true /*encoding*/, "\r"); - checkEncoding("iso-8859-1", false /*bom*/, true /*encoding*/, "\r"); - - checkEncoding("UTF-16", false /*bom*/, true /*encoding*/, "\r\n"); - checkEncoding("UTF_16LE", false /*bom*/, true /*encoding*/, "\r\n"); - checkEncoding("UTF_32", false /*bom*/, true /*encoding*/, "\r\n"); - checkEncoding("UTF_32LE", false /*bom*/, true /*encoding*/, "\r\n"); - checkEncoding("windows-1252", false /*bom*/, true /*encoding*/, "\r\n"); - checkEncoding("MacRoman", false /*bom*/, true /*encoding*/, "\r\n"); - checkEncoding("ISO-8859-1", false /*bom*/, true /*encoding*/, "\r\n"); - checkEncoding("iso-8859-1", false /*bom*/, true /*encoding*/, "\r\n"); - } -} diff --git a/common/src/test/java/com/android/utils/SdkUtilsTest.java b/common/src/test/java/com/android/utils/SdkUtilsTest.java deleted file mode 100644 index 030e1b7..0000000 --- a/common/src/test/java/com/android/utils/SdkUtilsTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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.utils; - -import junit.framework.TestCase; - -@SuppressWarnings("javadoc") -public class SdkUtilsTest extends TestCase { - public void testEndsWithIgnoreCase() { - assertTrue(SdkUtils.endsWithIgnoreCase("foo", "foo")); - assertTrue(SdkUtils.endsWithIgnoreCase("foo", "Foo")); - assertTrue(SdkUtils.endsWithIgnoreCase("foo", "foo")); - assertTrue(SdkUtils.endsWithIgnoreCase("Barfoo", "foo")); - assertTrue(SdkUtils.endsWithIgnoreCase("BarFoo", "foo")); - assertTrue(SdkUtils.endsWithIgnoreCase("BarFoo", "foO")); - - assertFalse(SdkUtils.endsWithIgnoreCase("foob", "foo")); - assertFalse(SdkUtils.endsWithIgnoreCase("foo", "fo")); - } - - public void testStartsWithIgnoreCase() { - assertTrue(SdkUtils.startsWithIgnoreCase("foo", "foo")); - assertTrue(SdkUtils.startsWithIgnoreCase("foo", "Foo")); - assertTrue(SdkUtils.startsWithIgnoreCase("foo", "foo")); - assertTrue(SdkUtils.startsWithIgnoreCase("barfoo", "bar")); - assertTrue(SdkUtils.startsWithIgnoreCase("BarFoo", "bar")); - assertTrue(SdkUtils.startsWithIgnoreCase("BarFoo", "bAr")); - - assertFalse(SdkUtils.startsWithIgnoreCase("bfoo", "foo")); - assertFalse(SdkUtils.startsWithIgnoreCase("fo", "foo")); - } - - public void testStartsWith() { - assertTrue(SdkUtils.startsWith("foo", 0, "foo")); - assertTrue(SdkUtils.startsWith("foo", 0, "Foo")); - assertTrue(SdkUtils.startsWith("Foo", 0, "foo")); - assertTrue(SdkUtils.startsWith("aFoo", 1, "foo")); - - assertFalse(SdkUtils.startsWith("aFoo", 0, "foo")); - assertFalse(SdkUtils.startsWith("aFoo", 2, "foo")); - } - - public void testEndsWith() { - assertTrue(SdkUtils.endsWith("foo", "foo")); - assertTrue(SdkUtils.endsWith("foobar", "obar")); - assertTrue(SdkUtils.endsWith("foobar", "bar")); - assertTrue(SdkUtils.endsWith("foobar", "ar")); - assertTrue(SdkUtils.endsWith("foobar", "r")); - assertTrue(SdkUtils.endsWith("foobar", "")); - - assertTrue(SdkUtils.endsWith(new StringBuilder("foobar"), "bar")); - assertTrue(SdkUtils.endsWith(new StringBuilder("foobar"), new StringBuffer("obar"))); - assertTrue(SdkUtils.endsWith("foobar", new StringBuffer("obar"))); - - assertFalse(SdkUtils.endsWith("foo", "fo")); - assertFalse(SdkUtils.endsWith("foobar", "Bar")); - assertFalse(SdkUtils.endsWith("foobar", "longfoobar")); - } - - public void testEndsWith2() { - assertTrue(SdkUtils.endsWith("foo", "foo".length(), "foo")); - assertTrue(SdkUtils.endsWith("foo", "fo".length(), "fo")); - assertTrue(SdkUtils.endsWith("foo", "f".length(), "f")); - } - - public void testStripWhitespace() { - assertEquals("foo", SdkUtils.stripWhitespace("foo")); - assertEquals("foobar", SdkUtils.stripWhitespace("foo bar")); - assertEquals("foobar", SdkUtils.stripWhitespace(" foo bar \n\t")); - } - - public void testWrap() { - String s = - "Hardcoding text attributes directly in layout files is bad for several reasons:\n" + - "\n" + - "* When creating configuration variations (for example for landscape or portrait)" + - "you have to repeat the actual text (and keep it up to date when making changes)\n" + - "\n" + - "* The application cannot be translated to other languages by just adding new " + - "translations for existing string resources."; - String wrapped = SdkUtils.wrap(s, 70, ""); - assertEquals( - "Hardcoding text attributes directly in layout files is bad for several\n" + - "reasons:\n" + - "\n" + - "* When creating configuration variations (for example for landscape or\n" + - "portrait)you have to repeat the actual text (and keep it up to date\n" + - "when making changes)\n" + - "\n" + - "* The application cannot be translated to other languages by just\n" + - "adding new translations for existing string resources.\n", - wrapped); - } - - public void testWrapPrefix() { - String s = - "Hardcoding text attributes directly in layout files is bad for several reasons:\n" + - "\n" + - "* When creating configuration variations (for example for landscape or portrait)" + - "you have to repeat the actual text (and keep it up to date when making changes)\n" + - "\n" + - "* The application cannot be translated to other languages by just adding new " + - "translations for existing string resources."; - String wrapped = SdkUtils.wrap(s, 70, " "); - assertEquals( - "Hardcoding text attributes directly in layout files is bad for several\n" + - " reasons:\n" + - " \n" + - " * When creating configuration variations (for example for\n" + - " landscape or portrait)you have to repeat the actual text (and keep\n" + - " it up to date when making changes)\n" + - " \n" + - " * The application cannot be translated to other languages by just\n" + - " adding new translations for existing string resources.\n", - wrapped); - } - - -} diff --git a/common/src/test/java/com/android/utils/XmlUtilsTest.java b/common/src/test/java/com/android/utils/XmlUtilsTest.java deleted file mode 100644 index edf0235..0000000 --- a/common/src/test/java/com/android/utils/XmlUtilsTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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.utils; - -import static com.android.SdkConstants.XMLNS; - -import com.android.SdkConstants; -import com.android.annotations.Nullable; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import junit.framework.TestCase; - -@SuppressWarnings("javadoc") -public class XmlUtilsTest extends TestCase { - public void testlookupNamespacePrefix() throws Exception { - // Setup - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - factory.setValidating(false); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document document = builder.newDocument(); - Element rootElement = document.createElement("root"); - Attr attr = document.createAttributeNS(SdkConstants.XMLNS_URI, - "xmlns:customPrefix"); - attr.setValue(SdkConstants.ANDROID_URI); - rootElement.getAttributes().setNamedItemNS(attr); - document.appendChild(rootElement); - Element root = document.getDocumentElement(); - root.appendChild(document.createTextNode(" ")); - Element foo = document.createElement("foo"); - root.appendChild(foo); - root.appendChild(document.createTextNode(" ")); - Element bar = document.createElement("bar"); - root.appendChild(bar); - Element baz = document.createElement("baz"); - root.appendChild(baz); - - String prefix = XmlUtils.lookupNamespacePrefix(baz, SdkConstants.ANDROID_URI); - assertEquals("customPrefix", prefix); - - prefix = XmlUtils.lookupNamespacePrefix(baz, - "http://schemas.android.com/tools", "tools", false); - assertEquals("tools", prefix); - - prefix = XmlUtils.lookupNamespacePrefix(baz, - "http://schemas.android.com/apk/res/my/pkg", "app", false); - assertEquals("app", prefix); - assertFalse(declaresNamespace(document, "http://schemas.android.com/apk/res/my/pkg")); - - prefix = XmlUtils.lookupNamespacePrefix(baz, - "http://schemas.android.com/apk/res/my/pkg", "app", true /*create*/); - assertEquals("app", prefix); - assertTrue(declaresNamespace(document, "http://schemas.android.com/apk/res/my/pkg")); - } - - private static boolean declaresNamespace(Document document, String uri) { - NamedNodeMap attributes = document.getDocumentElement().getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Attr attribute = (Attr) attributes.item(i); - String name = attribute.getName(); - if (name.startsWith(XMLNS) && uri.equals(attribute.getValue())) { - return true; - } - } - - return false; - } - - public void testToXmlAttributeValue() throws Exception { - assertEquals("", XmlUtils.toXmlAttributeValue("")); - assertEquals("foo", XmlUtils.toXmlAttributeValue("foo")); - assertEquals("foo<bar", XmlUtils.toXmlAttributeValue("foo<bar")); - assertEquals("foo>bar", XmlUtils.toXmlAttributeValue("foo>bar")); - - assertEquals(""", XmlUtils.toXmlAttributeValue("\"")); - assertEquals("'", XmlUtils.toXmlAttributeValue("'")); - assertEquals("foo"b''ar", - XmlUtils.toXmlAttributeValue("foo\"b''ar")); - assertEquals("<"'>&", XmlUtils.toXmlAttributeValue("<\"'>&")); - } - - public void testAppendXmlAttributeValue() throws Exception { - StringBuilder sb = new StringBuilder(); - XmlUtils.appendXmlAttributeValue(sb, "<\"'>&"); - assertEquals("<"'>&", sb.toString()); - } - - public void testToXmlTextValue() throws Exception { - assertEquals("<\"'>&", XmlUtils.toXmlTextValue("<\"'>&")); - } - - public void testAppendXmlTextValue() throws Exception { - StringBuilder sb = new StringBuilder(); - XmlUtils.appendXmlTextValue(sb, "<\"'>&"); - assertEquals("<\"'>&", sb.toString()); - } - - public void testNew() throws Exception { - Document doc = createEmptyPlainDocument(); - Element root = doc.createElement("myroot"); - doc.appendChild(root); - root.setAttribute("foo", "bar"); - root.setAttribute("baz", "baz"); - Element child = doc.createElement("mychild"); - root.appendChild(child); - Element child2 = doc.createElement("hasComment"); - root.appendChild(child2); - Node comment = doc.createComment("This is my comment"); - child2.appendChild(comment); - Element child3 = doc.createElement("hasText"); - root.appendChild(child3); - Node text = doc.createTextNode(" This is my text "); - child3.appendChild(text); - - String xml = XmlUtils.toXml(doc, false); - assertEquals( - "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + - "<myroot baz=\"baz\" foo=\"bar\">\n" + - " <mychild/>\n" + - " <hasComment>\n" + - " <!--\n" + - " This is my comment\n" + - " -->\n" + - " </hasComment>\n" + - " <hasText>\n" + - " This is my text\n" + - " </hasText>\n" + - "</myroot>\n", - xml); - - xml = XmlUtils.toXml(doc, true); - assertEquals( - "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + - "<myroot baz=\"baz\" foo=\"bar\"><mychild/><hasComment><!--This is my comment--></hasComment><hasText> This is my text </hasText></myroot>", - xml); - - } - - @Nullable - private static Document createEmptyPlainDocument() throws Exception { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - factory.setValidating(false); - factory.setIgnoringComments(true); - DocumentBuilder builder; - builder = factory.newDocumentBuilder(); - return builder.newDocument(); - } -} diff --git a/ddms/app/etc/manifest.txt b/ddms/app/etc/manifest.txt index 88249c4..f3d4fdd 100644 --- a/ddms/app/etc/manifest.txt +++ b/ddms/app/etc/manifest.txt @@ -1,3 +1,3 @@ Main-Class: com.android.ddms.Main -Class-Path: common.jar sdkstats.jar ddmlib.jar ddmuilib.jar swtmenubar.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.equinox.common_3.6.0.v20100503.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar osgi.jar +Class-Path: common.jar sdkstats.jar ddmlib.jar ddmuilib.jar swtmenubar.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.equinox.common_3.6.0.v20100503.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar osgi.jar guava-tools.jar diff --git a/ddms/libs/ddmlib/Android.mk b/ddms/libs/ddmlib/Android.mk index c479e79..c5ad99e 100644 --- a/ddms/libs/ddmlib/Android.mk +++ b/ddms/libs/ddmlib/Android.mk @@ -26,7 +26,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LIBRARIES := kxml2-2.3.0 LOCAL_PREBUILT_JAVA_LIBRARIES := \ - ../../../../prebuilts/devtools/$(LOCAL_MODULE)/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) + ../../../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) include $(BUILD_HOST_PREBUILT) diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatBufferChangeListener.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatBufferChangeListener.java index 1a547c7..2804629 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatBufferChangeListener.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatBufferChangeListener.java @@ -16,6 +16,8 @@ package com.android.ddmuilib.logcat; +import com.android.ddmlib.logcat.LogCatMessage; + import java.util.List; /** diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatMessageSelectionListener.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatMessageSelectionListener.java index 6e814b0..728b518 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatMessageSelectionListener.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatMessageSelectionListener.java @@ -15,6 +15,8 @@ */ package com.android.ddmuilib.logcat; +import com.android.ddmlib.logcat.LogCatMessage; + /** * Classes interested in listening to user selection of logcat * messages should implement this interface. diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilter.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilter.java deleted file mode 100644 index 7bdd98a..0000000 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilter.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * 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.ddmuilib.logcat; - -import com.android.ddmlib.Log.LogLevel; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -/** - * A Filter for logcat messages. A filter can be constructed to match - * different fields of a logcat message. It can then be queried to see if - * a message matches the filter's settings. - */ -public final class LogCatFilter { - private static final String PID_KEYWORD = "pid:"; //$NON-NLS-1$ - private static final String APP_KEYWORD = "app:"; //$NON-NLS-1$ - private static final String TAG_KEYWORD = "tag:"; //$NON-NLS-1$ - private static final String TEXT_KEYWORD = "text:"; //$NON-NLS-1$ - - private final String mName; - private final String mTag; - private final String mText; - private final String mPid; - private final String mAppName; - private final LogLevel mLogLevel; - - /** Indicates the number of messages that match this filter, but have not - * yet been read by the user. This is really metadata about this filter - * necessary for the UI. If we ever end up needing to store more metadata, - * then it is probably better to move it out into a separate class. */ - private int mUnreadCount; - - /** Indicates that this filter is transient, and should not be persisted - * across Eclipse sessions. */ - private boolean mTransient; - - private boolean mCheckPid; - private boolean mCheckAppName; - private boolean mCheckTag; - private boolean mCheckText; - - private Pattern mAppNamePattern; - private Pattern mTagPattern; - private Pattern mTextPattern; - - /** - * Construct a filter with the provided restrictions for the logcat message. All the text - * fields accept Java regexes as input, but ignore invalid regexes. Filters are saved and - * restored across Eclipse sessions unless explicitly marked transient using - * {@link LogCatFilter#setTransient}. - * @param name name for the filter - * @param tag value for the logcat message's tag field. - * @param text value for the logcat message's text field. - * @param pid value for the logcat message's pid field. - * @param appName value for the logcat message's app name field. - * @param logLevel value for the logcat message's log level. Only messages of - * higher priority will be accepted by the filter. - */ - public LogCatFilter(String name, String tag, String text, String pid, String appName, - LogLevel logLevel) { - mName = name.trim(); - mTag = tag.trim(); - mText = text.trim(); - mPid = pid.trim(); - mAppName = appName.trim(); - mLogLevel = logLevel; - - mUnreadCount = 0; - - // By default, all filters are persistent. Transient filters should explicitly - // mark it so by calling setTransient. - mTransient = false; - - mCheckPid = mPid.length() != 0; - - if (mAppName.length() != 0) { - try { - mAppNamePattern = Pattern.compile(mAppName, getPatternCompileFlags(mAppName)); - mCheckAppName = true; - } catch (PatternSyntaxException e) { - mCheckAppName = false; - } - } - - if (mTag.length() != 0) { - try { - mTagPattern = Pattern.compile(mTag, getPatternCompileFlags(mTag)); - mCheckTag = true; - } catch (PatternSyntaxException e) { - mCheckTag = false; - } - } - - if (mText.length() != 0) { - try { - mTextPattern = Pattern.compile(mText, getPatternCompileFlags(mText)); - mCheckText = true; - } catch (PatternSyntaxException e) { - mCheckText = false; - } - } - } - - /** - * Obtain the flags to pass to {@link Pattern#compile(String, int)}. This method - * tries to figure out whether case sensitive matching should be used. It is based on - * the following heuristic: if the regex has an upper case character, then the match - * will be case sensitive. Otherwise it will be case insensitive. - */ - private int getPatternCompileFlags(String regex) { - for (char c : regex.toCharArray()) { - if (Character.isUpperCase(c)) { - return 0; - } - } - - return Pattern.CASE_INSENSITIVE; - } - - /** - * Construct a list of {@link LogCatFilter} objects by decoding the query. - * @param query encoded search string. The query is simply a list of words (can be regexes) - * a user would type in a search bar. These words are searched for in the text field of - * each collected logcat message. To search in a different field, the word could be prefixed - * with a keyword corresponding to the field name. Currently, the following keywords are - * supported: "pid:", "tag:" and "text:". Invalid regexes are ignored. - * @param minLevel minimum log level to match - * @return list of filter settings that fully match the given query - */ - public static List<LogCatFilter> fromString(String query, LogLevel minLevel) { - List<LogCatFilter> filterSettings = new ArrayList<LogCatFilter>(); - - for (String s : query.trim().split(" ")) { - String tag = ""; - String text = ""; - String pid = ""; - String app = ""; - - if (s.startsWith(PID_KEYWORD)) { - pid = s.substring(PID_KEYWORD.length()); - } else if (s.startsWith(APP_KEYWORD)) { - app = s.substring(APP_KEYWORD.length()); - } else if (s.startsWith(TAG_KEYWORD)) { - tag = s.substring(TAG_KEYWORD.length()); - } else { - if (s.startsWith(TEXT_KEYWORD)) { - text = s.substring(TEXT_KEYWORD.length()); - } else { - text = s; - } - } - filterSettings.add(new LogCatFilter("livefilter-" + s, - tag, text, pid, app, minLevel)); - } - - return filterSettings; - } - - public String getName() { - return mName; - } - - public String getTag() { - return mTag; - } - - public String getText() { - return mText; - } - - public String getPid() { - return mPid; - } - - public String getAppName() { - return mAppName; - } - - public LogLevel getLogLevel() { - return mLogLevel; - } - - /** - * Check whether a given message will make it through this filter. - * @param m message to check - * @return true if the message matches the filter's conditions. - */ - public boolean matches(LogCatMessage m) { - /* filter out messages of a lower priority */ - if (m.getLogLevel().getPriority() < mLogLevel.getPriority()) { - return false; - } - - /* if pid filter is enabled, filter out messages whose pid does not match - * the filter's pid */ - if (mCheckPid && !m.getPid().equals(mPid)) { - return false; - } - - /* if app name filter is enabled, filter out messages not matching the app name */ - if (mCheckAppName) { - Matcher matcher = mAppNamePattern.matcher(m.getAppName()); - if (!matcher.find()) { - return false; - } - } - - /* if tag filter is enabled, filter out messages not matching the tag */ - if (mCheckTag) { - Matcher matcher = mTagPattern.matcher(m.getTag()); - if (!matcher.find()) { - return false; - } - } - - if (mCheckText) { - Matcher matcher = mTextPattern.matcher(m.getMessage()); - if (!matcher.find()) { - return false; - } - } - - return true; - } - - /** - * Update the unread count based on new messages received. The unread count - * is incremented by the count of messages in the received list that will be - * accepted by this filter. - * @param newMessages list of new messages. - */ - public void updateUnreadCount(List<LogCatMessage> newMessages) { - for (LogCatMessage m : newMessages) { - if (matches(m)) { - mUnreadCount++; - } - } - } - - /** - * Reset count of unread messages. - */ - public void resetUnreadCount() { - mUnreadCount = 0; - } - - /** - * Get current value for the unread message counter. - */ - public int getUnreadCount() { - return mUnreadCount; - } - - /** Make this filter transient: It will not be persisted across sessions. */ - public void setTransient() { - mTransient = true; - } - - public boolean isTransient() { - return mTransient; - } -} diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterContentProvider.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterContentProvider.java index 68c08d4..629b0e0 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterContentProvider.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterContentProvider.java @@ -15,6 +15,8 @@ */ package com.android.ddmuilib.logcat; +import com.android.ddmlib.logcat.LogCatFilter; + import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.Viewer; diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterData.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterData.java new file mode 100644 index 0000000..dbc34d8 --- /dev/null +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterData.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 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.ddmuilib.logcat; + +import com.android.ddmlib.logcat.LogCatFilter; +import com.android.ddmlib.logcat.LogCatMessage; + +import java.util.List; + +public class LogCatFilterData { + private final LogCatFilter mFilter; + + /** Indicates the number of messages that match this filter, but have not + * yet been read by the user. This is really metadata about this filter + * necessary for the UI. If we ever end up needing to store more metadata, + * then it is probably better to move it out into a separate class. */ + private int mUnreadCount; + + /** Indicates that this filter is transient, and should not be persisted + * across Eclipse sessions. */ + private boolean mTransient; + + public LogCatFilterData(LogCatFilter f) { + mFilter = f; + + // By default, all filters are persistent. Transient filters should explicitly + // mark it so by calling setTransient. + mTransient = false; + } + + /** + * Update the unread count based on new messages received. The unread count + * is incremented by the count of messages in the received list that will be + * accepted by this filter. + * @param newMessages list of new messages. + */ + public void updateUnreadCount(List<LogCatMessage> newMessages) { + for (LogCatMessage m : newMessages) { + if (mFilter.matches(m)) { + mUnreadCount++; + } + } + } + + /** + * Reset count of unread messages. + */ + public void resetUnreadCount() { + mUnreadCount = 0; + } + + /** + * Get current value for the unread message counter. + */ + public int getUnreadCount() { + return mUnreadCount; + } + + /** Make this filter transient: It will not be persisted across sessions. */ + public void setTransient() { + mTransient = true; + } + + public boolean isTransient() { + return mTransient; + } +} diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterLabelProvider.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterLabelProvider.java index 59e236c..fe24ddd 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterLabelProvider.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterLabelProvider.java @@ -15,15 +15,25 @@ */ package com.android.ddmuilib.logcat; +import com.android.ddmlib.logcat.LogCatFilter; + import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.swt.graphics.Image; +import java.util.Map; + /** * A JFace label provider for the LogCat filters. It expects elements of type * {@link LogCatFilter}. */ public final class LogCatFilterLabelProvider extends LabelProvider implements ITableLabelProvider { + private Map<LogCatFilter, LogCatFilterData> mFilterData; + + public LogCatFilterLabelProvider(Map<LogCatFilter, LogCatFilterData> filterData) { + mFilterData = filterData; + } + @Override public Image getColumnImage(Object arg0, int arg1) { return null; @@ -42,11 +52,12 @@ public final class LogCatFilterLabelProvider extends LabelProvider implements IT } LogCatFilter f = (LogCatFilter) element; + LogCatFilterData fd = mFilterData.get(f); - if (f.getUnreadCount() == 0) { - return f.getName(); + if (fd != null && fd.getUnreadCount() > 0) { + return String.format("%s (%d)", f.getName(), fd.getUnreadCount()); } else { - return String.format("%s (%d)", f.getName(), f.getUnreadCount()); + return f.getName(); } } } diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettingsSerializer.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettingsSerializer.java index 12fbdfa..de35162 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettingsSerializer.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettingsSerializer.java @@ -16,9 +16,11 @@ package com.android.ddmuilib.logcat; import com.android.ddmlib.Log.LogLevel; +import com.android.ddmlib.logcat.LogCatFilter; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Class to help save/restore user created filters. @@ -49,14 +51,17 @@ public final class LogCatFilterSettingsSerializer { * {@link LogCatFilterSettingsSerializer#decodeFromPreferenceString(String)} for the * reverse operation. * @param filters list of filters to save. + * @param filterData mapping from filter to per filter UI data * @return an encoded string that can be saved in Eclipse preference store. The encoded string * is of a list of key:'value' pairs. */ - public String encodeToPreferenceString(List<LogCatFilter> filters) { + public String encodeToPreferenceString(List<LogCatFilter> filters, + Map<LogCatFilter, LogCatFilterData> filterData) { StringBuffer sb = new StringBuffer(); for (LogCatFilter f : filters) { - if (f.isTransient()) { + LogCatFilterData fd = filterData.get(f); + if (fd != null && fd.isTransient()) { // do not persist transient filters continue; } diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatMessage.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatMessage.java deleted file mode 100644 index aea4ead..0000000 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatMessage.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.ddmuilib.logcat; - -import com.android.ddmlib.Log.LogLevel; - -/** - * Model a single log message output from {@code logcat -v long}. - * A logcat message has a {@link LogLevel}, the pid (process id) of the process - * generating the message, the time at which the message was generated, and - * the tag and message itself. - */ -public final class LogCatMessage { - private final LogLevel mLogLevel; - private final String mPid; - private final String mTid; - private final String mAppName; - private final String mTag; - private final String mTime; - private final String mMessage; - - /** - * Construct an immutable log message object. - */ - public LogCatMessage(LogLevel logLevel, String pid, String tid, String appName, - String tag, String time, String msg) { - mLogLevel = logLevel; - mPid = pid; - mAppName = appName; - mTag = tag; - mTime = time; - mMessage = msg; - - long tidValue; - try { - // Thread id's may be in hex on some platforms. - // Decode and store them in radix 10. - tidValue = Long.decode(tid.trim()); - } catch (NumberFormatException e) { - tidValue = -1; - } - - mTid = Long.toString(tidValue); - } - - public LogLevel getLogLevel() { - return mLogLevel; - } - - public String getPid() { - return mPid; - } - - public String getTid() { - return mTid; - } - - public String getAppName() { - return mAppName; - } - - public String getTag() { - return mTag; - } - - public String getTime() { - return mTime; - } - - public String getMessage() { - return mMessage; - } - - @Override - public String toString() { - return mTime + ": " - + mLogLevel.getPriorityLetter() + "/" - + mTag + "(" - + mPid + "): " - + mMessage; - } -} diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatMessageList.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatMessageList.java index 080dbc1..c5cd548 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatMessageList.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatMessageList.java @@ -16,6 +16,8 @@ package com.android.ddmuilib.logcat; +import com.android.ddmlib.logcat.LogCatMessage; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatMessageParser.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatMessageParser.java deleted file mode 100644 index b69a433..0000000 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatMessageParser.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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.ddmuilib.logcat; - -import com.android.ddmlib.Log.LogLevel; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Class to parse raw output of {@code adb logcat -v long} to {@link LogCatMessage} objects. - */ -public final class LogCatMessageParser { - private LogLevel mCurLogLevel = LogLevel.WARN; - private String mCurPid = "?"; - private String mCurTid = "?"; - private String mCurTag = "?"; - private String mCurTime = "?:??"; - - /** - * This pattern is meant to parse the first line of a log message with the option - * 'logcat -v long'. The first line represents the date, tag, severity, etc.. while the - * following lines are the message (can be several lines).<br> - * This first line looks something like:<br> - * {@code "[ 00-00 00:00:00.000 <pid>:0x<???> <severity>/<tag>]"} - * <br> - * Note: severity is one of V, D, I, W, E, A? or F. However, there doesn't seem to be - * a way to actually generate an A (assert) message. Log.wtf is supposed to generate - * a message with severity A, however it generates the undocumented F level. In - * such a case, the parser will change the level from F to A.<br> - * Note: the fraction of second value can have any number of digit.<br> - * Note: the tag should be trimmed as it may have spaces at the end. - */ - private static Pattern sLogHeaderPattern = Pattern.compile( - "^\\[\\s(\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d\\.\\d+)" - + "\\s+(\\d*):\\s*(\\S+)\\s([VDIWEAF])/(.*)\\]$"); - - /** - * Parse a list of strings into {@link LogCatMessage} objects. This method - * maintains state from previous calls regarding the last seen header of - * logcat messages. - * @param lines list of raw strings obtained from logcat -v long - * @param pidToNameMapper mapper to obtain the app name given a pid - * @return list of LogMessage objects parsed from the input - */ - public List<LogCatMessage> processLogLines(String[] lines, - LogCatPidToNameMapper pidToNameMapper) { - List<LogCatMessage> messages = new ArrayList<LogCatMessage>(lines.length); - - for (String line : lines) { - if (line.length() == 0) { - continue; - } - - Matcher matcher = sLogHeaderPattern.matcher(line); - if (matcher.matches()) { - mCurTime = matcher.group(1); - mCurPid = matcher.group(2); - mCurTid = matcher.group(3); - mCurLogLevel = LogLevel.getByLetterString(matcher.group(4)); - mCurTag = matcher.group(5).trim(); - - /* LogLevel doesn't support messages with severity "F". Log.wtf() is supposed - * to generate "A", but generates "F". */ - if (mCurLogLevel == null && matcher.group(4).equals("F")) { - mCurLogLevel = LogLevel.ASSERT; - } - } else { - LogCatMessage m = new LogCatMessage(mCurLogLevel, mCurPid, mCurTid, - pidToNameMapper.getName(mCurPid), - mCurTag, mCurTime, line); - messages.add(m); - } - } - - return messages; - } -} diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java index 4f27f02..bda742c 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java @@ -19,6 +19,8 @@ package com.android.ddmuilib.logcat; import com.android.ddmlib.DdmConstants; import com.android.ddmlib.IDevice; import com.android.ddmlib.Log.LogLevel; +import com.android.ddmlib.logcat.LogCatFilter; +import com.android.ddmlib.logcat.LogCatMessage; import com.android.ddmuilib.AbstractBufferFindTarget; import com.android.ddmuilib.FindDialog; import com.android.ddmuilib.ITableFocusListener; @@ -85,6 +87,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -160,6 +164,7 @@ public final class LogCatPanel extends SelectionDependentPanel private IPreferenceStore mPrefStore; private List<LogCatFilter> mLogCatFilters; + private Map<LogCatFilter, LogCatFilterData> mLogCatFilterData; private int mCurrentSelectedFilterIndex; private ToolItem mNewFilterToolItem; @@ -236,18 +241,25 @@ public final class LogCatPanel extends SelectionDependentPanel private void initializeFilters() { mLogCatFilters = new ArrayList<LogCatFilter>(); + mLogCatFilterData = new ConcurrentHashMap<LogCatFilter, LogCatFilterData>(); /* add default filter matching all messages */ String tag = ""; String text = ""; String pid = ""; String app = ""; - mLogCatFilters.add(new LogCatFilter("All messages (no filters)", - tag, text, pid, app, LogLevel.VERBOSE)); + LogCatFilter defaultFilter = new LogCatFilter("All messages (no filters)", + tag, text, pid, app, LogLevel.VERBOSE); + + mLogCatFilters.add(defaultFilter); + mLogCatFilterData.put(defaultFilter, new LogCatFilterData(defaultFilter)); /* restore saved filters from prefStore */ List<LogCatFilter> savedFilters = getSavedFilters(); - mLogCatFilters.addAll(savedFilters); + for (LogCatFilter f: savedFilters) { + mLogCatFilters.add(f); + mLogCatFilterData.put(f, new LogCatFilterData(f)); + } } private void setupDefaultPreferences() { @@ -323,7 +335,7 @@ public final class LogCatPanel extends SelectionDependentPanel /* save all filter settings except the first one which is the default */ String e = serializer.encodeToPreferenceString( - mLogCatFilters.subList(1, mLogCatFilters.size())); + mLogCatFilters.subList(1, mLogCatFilters.size()), mLogCatFilterData); mPrefStore.setValue(LOGCAT_FILTERS_LIST, e); } @@ -348,7 +360,8 @@ public final class LogCatPanel extends SelectionDependentPanel // When switching between devices, existing filter match count should be reset. for (LogCatFilter f : mLogCatFilters) { - f.resetUnreadCount(); + LogCatFilterData fd = mLogCatFilterData.get(f); + fd.resetUnreadCount(); } } @@ -478,6 +491,7 @@ public final class LogCatPanel extends SelectionDependentPanel LogLevel.getByString(d.getLogLevel())); mLogCatFilters.add(f); + mLogCatFilterData.put(f, new LogCatFilterData(f)); mFiltersTableViewer.refresh(); /* select the newly added entry */ @@ -489,8 +503,7 @@ public final class LogCatPanel extends SelectionDependentPanel } private void addNewFilter() { - addNewFilter("", "", "", - "", LogLevel.VERBOSE); + addNewFilter("", "", "", "", LogLevel.VERBOSE); } private void deleteSelectedFilter() { @@ -500,7 +513,10 @@ public final class LogCatPanel extends SelectionDependentPanel return; } + LogCatFilter f = mLogCatFilters.get(selectedIndex); mLogCatFilters.remove(selectedIndex); + mLogCatFilterData.remove(f); + mFiltersTableViewer.refresh(); mFiltersTableViewer.getTable().setSelection(selectedIndex - 1); @@ -551,6 +567,10 @@ public final class LogCatPanel extends SelectionDependentPanel if (f == null) { f = createTransientAppFilter(appName); mLogCatFilters.add(f); + + LogCatFilterData fd = new LogCatFilterData(f); + fd.setTransient(); + mLogCatFilterData.put(f, fd); } selectFilterAt(mLogCatFilters.indexOf(f)); @@ -558,7 +578,8 @@ public final class LogCatPanel extends SelectionDependentPanel private LogCatFilter findTransientAppFilter(String appName) { for (LogCatFilter f : mLogCatFilters) { - if (f.isTransient() && f.getAppName().equals(appName)) { + LogCatFilterData fd = mLogCatFilterData.get(f); + if (fd != null && fd.isTransient() && f.getAppName().equals(appName)) { return f; } } @@ -572,7 +593,6 @@ public final class LogCatPanel extends SelectionDependentPanel "", appName, LogLevel.VERBOSE); - f.setTransient(); return f; } @@ -594,7 +614,7 @@ public final class LogCatPanel extends SelectionDependentPanel mFiltersTableViewer = new TableViewer(table); mFiltersTableViewer.setContentProvider(new LogCatFilterContentProvider()); - mFiltersTableViewer.setLabelProvider(new LogCatFilterLabelProvider()); + mFiltersTableViewer.setLabelProvider(new LogCatFilterLabelProvider(mLogCatFilterData)); mFiltersTableViewer.setInput(mLogCatFilters); mFiltersTableViewer.getTable().addSelectionListener(new SelectionAdapter() { @@ -671,6 +691,7 @@ public final class LogCatPanel extends SelectionDependentPanel if (mReceiver != null) { mReceiver.clearMessages(); refreshLogCatTable(); + resetUnreadCountForAllFilters(); // the filters view is not cleared unless the filters are re-applied. updateAppliedFilters(); @@ -1091,13 +1112,15 @@ public final class LogCatPanel extends SelectionDependentPanel mCurrentSelectedFilterIndex = idx; - resetUnreadCountForSelectedFilter(); + resetUnreadCountForAllFilters(); updateFiltersToolBar(); updateAppliedFilters(); } - private void resetUnreadCountForSelectedFilter() { - mLogCatFilters.get(mCurrentSelectedFilterIndex).resetUnreadCount(); + private void resetUnreadCountForAllFilters() { + for (LogCatFilterData fd: mLogCatFilterData.values()) { + fd.resetUnreadCount(); + } refreshFiltersTable(); } @@ -1142,6 +1165,8 @@ public final class LogCatPanel extends SelectionDependentPanel @Override public void bufferChanged(List<LogCatMessage> addedMessages, List<LogCatMessage> deletedMessages) { + updateUnreadCount(addedMessages); + refreshFiltersTable(); synchronized (mLogBuffer) { addedMessages = applyCurrentFilters(addedMessages); @@ -1152,8 +1177,6 @@ public final class LogCatPanel extends SelectionDependentPanel } refreshLogCatTable(); - updateUnreadCount(addedMessages); - refreshFiltersTable(); } private void reloadLogBuffer() { @@ -1184,7 +1207,9 @@ public final class LogCatPanel extends SelectionDependentPanel /* no need to update unread count for currently selected filter */ continue; } - mLogCatFilters.get(i).updateUnreadCount(receivedMessages); + LogCatFilter f = mLogCatFilters.get(i); + LogCatFilterData fd = mLogCatFilterData.get(f); + fd.updateUnreadCount(receivedMessages); } } @@ -1328,7 +1353,9 @@ public final class LogCatPanel extends SelectionDependentPanel Display.getDefault().asyncExec(new Runnable() { @Override public void run() { - startScrollBarMonitor(mTable.getVerticalBar()); + if (!mTable.isDisposed()) { + startScrollBarMonitor(mTable.getVerticalBar()); + } } }); } @@ -1372,7 +1399,9 @@ public final class LogCatPanel extends SelectionDependentPanel /** Scroll to the last line. */ private void scrollToLatestLog() { - mTable.setTopIndex(mTable.getItemCount() - 1); + if (!mTable.isDisposed()) { + mTable.setTopIndex(mTable.getItemCount() - 1); + } } /** diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPidToNameMapper.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPidToNameMapper.java deleted file mode 100644 index a4455d0..0000000 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPidToNameMapper.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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.ddmuilib.logcat; - -import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; -import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; -import com.android.ddmlib.Client; -import com.android.ddmlib.ClientData; -import com.android.ddmlib.IDevice; - -import java.util.HashMap; -import java.util.Map; - -/** - * This class maintains a mapping between the PID and the application name for all - * running apps on a device. It does this by implementing callbacks to two events: - * {@link AndroidDebugBridge.IDeviceChangeListener} and - * {@link AndroidDebugBridge.IClientChangeListener}. - */ -public class LogCatPidToNameMapper { - /** Default name used when the actual name cannot be determined. */ - public static final String UNKNOWN_APP = ""; - - private IClientChangeListener mClientChangeListener; - private IDeviceChangeListener mDeviceChangeListener; - private IDevice mDevice; - private Map<String, String> mPidToName; - - public LogCatPidToNameMapper(IDevice device) { - mDevice = device; - mClientChangeListener = constructClientChangeListener(); - AndroidDebugBridge.addClientChangeListener(mClientChangeListener); - - mDeviceChangeListener = constructDeviceChangeListener(); - AndroidDebugBridge.addDeviceChangeListener(mDeviceChangeListener); - - mPidToName = new HashMap<String, String>(); - - updateClientList(device); - } - - private IClientChangeListener constructClientChangeListener() { - return new IClientChangeListener() { - @Override - public void clientChanged(Client client, int changeMask) { - if ((changeMask & Client.CHANGE_NAME) == Client.CHANGE_NAME) { - ClientData cd = client.getClientData(); - updateClientName(cd); - } - } - }; - } - - private void updateClientName(ClientData cd) { - String name = cd.getClientDescription(); - if (name != null) { - int pid = cd.getPid(); - if (mPidToName != null) { - mPidToName.put(Integer.toString(pid), name); - } - } - } - - private IDeviceChangeListener constructDeviceChangeListener() { - return new IDeviceChangeListener() { - @Override - public void deviceDisconnected(IDevice device) { - } - - @Override - public void deviceConnected(IDevice device) { - } - - @Override - public void deviceChanged(IDevice device, int changeMask) { - if (changeMask == IDevice.CHANGE_CLIENT_LIST) { - updateClientList(device); - } - } - }; - } - - private void updateClientList(IDevice device) { - if (mDevice == null) { - return; - } - - if (!mDevice.equals(device)) { - return; - } - - mPidToName = new HashMap<String, String>(); - for (Client c : device.getClients()) { - ClientData cd = c.getClientData(); - String name = cd.getClientDescription(); - int pid = cd.getPid(); - - /* The name will be null for apps that have just been created. - * In such a case, we fill in the default name, and wait for the - * clientChangeListener to do the update with the correct name. - */ - if (name == null) { - name = UNKNOWN_APP; - } - - mPidToName.put(Integer.toString(pid), name); - } - } - - /** - * Get the application name corresponding to given pid. - * @param pid application's pid - * @return application name if available, else {@link LogCatPidToNameMapper#UNKNOWN_APP}. - */ - public String getName(String pid) { - String name = mPidToName.get(pid); - return name != null ? name : UNKNOWN_APP; - } -} diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatReceiver.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatReceiver.java index da3e86f..a85cd03 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatReceiver.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatReceiver.java @@ -17,10 +17,10 @@ package com.android.ddmuilib.logcat; import com.android.ddmlib.IDevice; -import com.android.ddmlib.IShellOutputReceiver; -import com.android.ddmlib.Log; import com.android.ddmlib.Log.LogLevel; -import com.android.ddmlib.MultiLineReceiver; +import com.android.ddmlib.logcat.LogCatListener; +import com.android.ddmlib.logcat.LogCatMessage; +import com.android.ddmlib.logcat.LogCatReceiverTask; import org.eclipse.jface.preference.IPreferenceStore; @@ -33,19 +33,15 @@ import java.util.Set; * A class to monitor a device for logcat messages. It stores the received * log messages in a circular buffer. */ -public final class LogCatReceiver { - private static final String LOGCAT_COMMAND = "logcat -v long"; - private static final int DEVICE_POLL_INTERVAL_MSEC = 1000; +public final class LogCatReceiver implements LogCatListener { private static LogCatMessage DEVICE_DISCONNECTED_MESSAGE = new LogCatMessage(LogLevel.ERROR, "", "", "", "", "", "Device disconnected"); private LogCatMessageList mLogMessages; private IDevice mCurrentDevice; - private LogCatOutputReceiver mCurrentLogCatOutputReceiver; + private LogCatReceiverTask mLogCatReceiverTask; private Set<ILogCatBufferChangeListener> mLogCatMessageListeners; - private LogCatMessageParser mLogCatMessageParser; - private LogCatPidToNameMapper mPidToNameMapper; private IPreferenceStore mPrefStore; /** @@ -61,9 +57,6 @@ public final class LogCatReceiver { mPrefStore = prefStore; mLogCatMessageListeners = new HashSet<ILogCatBufferChangeListener>(); - mLogCatMessageParser = new LogCatMessageParser(); - mPidToNameMapper = new LogCatPidToNameMapper(mCurrentDevice); - mLogMessages = new LogCatMessageList(getFifoSize()); startReceiverThread(); @@ -73,13 +66,14 @@ public final class LogCatReceiver { * Stop receiving messages from currently active device. */ public void stop() { - if (mCurrentLogCatOutputReceiver != null) { + if (mLogCatReceiverTask != null) { /* stop the current logcat command */ - mCurrentLogCatOutputReceiver.mIsCancelled = true; - mCurrentLogCatOutputReceiver = null; + mLogCatReceiverTask.removeLogCatListener(this); + mLogCatReceiverTask.stop(); + mLogCatReceiverTask = null; // add a message to the log indicating that the device has been disconnected. - processLogMessages(Collections.singletonList(DEVICE_DISCONNECTED_MESSAGE)); + log(Collections.singletonList(DEVICE_DISCONNECTED_MESSAGE)); } mCurrentDevice = null; @@ -91,85 +85,26 @@ public final class LogCatReceiver { } private void startReceiverThread() { - mCurrentLogCatOutputReceiver = new LogCatOutputReceiver(); - - Thread t = new Thread(new Runnable() { - @Override - public void run() { - /* wait while the device comes online */ - while (mCurrentDevice != null && !mCurrentDevice.isOnline()) { - try { - Thread.sleep(DEVICE_POLL_INTERVAL_MSEC); - } catch (InterruptedException e) { - return; - } - } - - try { - if (mCurrentDevice != null) { - mCurrentDevice.executeShellCommand(LOGCAT_COMMAND, - mCurrentLogCatOutputReceiver, 0); - } - } catch (Exception e) { - /* There are 4 possible exceptions: TimeoutException, - * AdbCommandRejectedException, ShellCommandUnresponsiveException and - * IOException. In case of any of them, the only recourse is to just - * log this unexpected situation and move on. - */ - Log.e("Unexpected error while launching logcat. Try reselecting the device.", - e); - } - } - }); - t.setName("LogCat output receiver for " + mCurrentDevice.getSerialNumber()); - t.start(); - } - - /** - * LogCatOutputReceiver implements {@link MultiLineReceiver#processNewLines(String[])}, - * which is called whenever there is output from logcat. It simply redirects this output - * to {@link LogCatReceiver#processLogLines(String[])}. This class is expected to be - * used from a different thread, and the only way to stop that thread is by using the - * {@link LogCatOutputReceiver#mIsCancelled} variable. - * See {@link IDevice#executeShellCommand(String, IShellOutputReceiver, int)} for more - * details. - */ - private class LogCatOutputReceiver extends MultiLineReceiver { - private boolean mIsCancelled; - - public LogCatOutputReceiver() { - setTrimLine(false); + if (mCurrentDevice == null) { + return; } - /** Implements {@link IShellOutputReceiver#isCancelled() }. */ - @Override - public boolean isCancelled() { - return mIsCancelled; - } + mLogCatReceiverTask = new LogCatReceiverTask(mCurrentDevice); + mLogCatReceiverTask.addLogCatListener(this); - @Override - public void processNewLines(String[] lines) { - if (!mIsCancelled) { - processLogLines(lines); - } - } - } - - private void processLogLines(String[] lines) { - List<LogCatMessage> newMessages = mLogCatMessageParser.processLogLines(lines, - mPidToNameMapper); - processLogMessages(newMessages); + Thread t = new Thread(mLogCatReceiverTask); + t.setName("LogCat output receiver for " + mCurrentDevice.getSerialNumber()); + t.start(); } - private void processLogMessages(List<LogCatMessage> newMessages) { - if (newMessages.size() > 0) { - List<LogCatMessage> deletedMessages; - synchronized (mLogMessages) { - deletedMessages = mLogMessages.ensureSpace(newMessages.size()); - mLogMessages.appendMessages(newMessages); - } - sendLogChangedEvent(newMessages, deletedMessages); + @Override + public void log(List<LogCatMessage> newMessages) { + List<LogCatMessage> deletedMessages; + synchronized (mLogMessages) { + deletedMessages = mLogMessages.ensureSpace(newMessages.size()); + mLogMessages.appendMessages(newMessages); } + sendLogChangedEvent(newMessages, deletedMessages); } /** diff --git a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatFilterSettingsSerializerTest.java b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatFilterSettingsSerializerTest.java index 0fc0c76..e6c0e76 100644 --- a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatFilterSettingsSerializerTest.java +++ b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatFilterSettingsSerializerTest.java @@ -16,8 +16,10 @@ package com.android.ddmuilib.logcat; import com.android.ddmlib.Log.LogLevel; +import com.android.ddmlib.logcat.LogCatFilter; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import junit.framework.TestCase; @@ -34,7 +36,8 @@ public class LogCatFilterSettingsSerializerTest extends TestCase { LogLevel.ERROR); LogCatFilterSettingsSerializer serializer = new LogCatFilterSettingsSerializer(); - String s = serializer.encodeToPreferenceString(Arrays.asList(fs)); + String s = serializer.encodeToPreferenceString(Arrays.asList(fs), + new HashMap<LogCatFilter, LogCatFilterData>()); List<LogCatFilter> decodedFiltersList = serializer.decodeFromPreferenceString(s); assertEquals(1, decodedFiltersList.size()); @@ -57,10 +60,14 @@ public class LogCatFilterSettingsSerializerTest extends TestCase { "123", //$NON-NLS-1$ "TestAppName.*", //$NON-NLS-1$ LogLevel.ERROR); - fs.setTransient(); + LogCatFilterData fd = new LogCatFilterData(fs); + fd.setTransient(); + HashMap<LogCatFilter, LogCatFilterData> fdMap = + new HashMap<LogCatFilter, LogCatFilterData>(); + fdMap.put(fs, fd); LogCatFilterSettingsSerializer serializer = new LogCatFilterSettingsSerializer(); - String s = serializer.encodeToPreferenceString(Arrays.asList(fs)); + String s = serializer.encodeToPreferenceString(Arrays.asList(fs), fdMap); List<LogCatFilter> decodedFiltersList = serializer.decodeFromPreferenceString(s); assertEquals(0, decodedFiltersList.size()); diff --git a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatFilterTest.java b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatFilterTest.java deleted file mode 100644 index 7fedb08..0000000 --- a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatFilterTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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.ddmuilib.logcat; - -import com.android.ddmlib.Log.LogLevel; - -import java.util.List; - -import junit.framework.TestCase; - -public class LogCatFilterTest extends TestCase { - public void testFilterByLogLevel() { - LogCatFilter filter = new LogCatFilter("", - "", "", "", "", LogLevel.DEBUG); - - /* filter message below filter's log level */ - LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "", "", "", ""); - assertEquals(false, filter.matches(msg)); - - /* do not filter message above filter's log level */ - msg = new LogCatMessage(LogLevel.ERROR, - "", "", "", "", "", ""); - assertEquals(true, filter.matches(msg)); - } - - public void testFilterByPid() { - LogCatFilter filter = new LogCatFilter("", - "", "", "123", "", LogLevel.VERBOSE); - - /* show message with pid matching filter */ - LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, - "123", "", "", "", "", ""); - assertEquals(true, filter.matches(msg)); - - /* don't show message with pid not matching filter */ - msg = new LogCatMessage(LogLevel.VERBOSE, - "12", "", "", "", "", ""); - assertEquals(false, filter.matches(msg)); - } - - public void testFilterByAppNameRegex() { - LogCatFilter filter = new LogCatFilter("", - "", "", "", "dalvik.*", LogLevel.VERBOSE); - - /* show message with pid matching filter */ - LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "dalvikvm1", "", "", ""); - assertEquals(true, filter.matches(msg)); - - /* don't show message with pid not matching filter */ - msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "system", "", "", ""); - assertEquals(false, filter.matches(msg)); - } - - public void testFilterByTagRegex() { - LogCatFilter filter = new LogCatFilter("", - "tag.*", "", "", "", LogLevel.VERBOSE); - - /* show message with tag matching filter */ - LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "", "tag123", "", ""); - assertEquals(true, filter.matches(msg)); - - msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "", "ta123", "", ""); - assertEquals(false, filter.matches(msg)); - } - - public void testFilterByTextRegex() { - LogCatFilter filter = new LogCatFilter("", - "", "text.*", "", "", LogLevel.VERBOSE); - - /* show message with text matching filter */ - LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "", "", "", "text123"); - assertEquals(true, filter.matches(msg)); - - msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "", "", "", "te123"); - assertEquals(false, filter.matches(msg)); - } - - public void testMatchingText() { - LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "", "", "", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "message with word1 and word2"); //$NON-NLS-1$ - assertEquals(true, search("word1 with", msg)); //$NON-NLS-1$ - assertEquals(true, search("text:w.* ", msg)); //$NON-NLS-1$ - assertEquals(false, search("absent", msg)); //$NON-NLS-1$ - } - - public void testTagKeyword() { - LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "", "tag", "", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "sample message"); //$NON-NLS-1$ - assertEquals(false, search("t.*", msg)); //$NON-NLS-1$ - assertEquals(true, search("tag:t.*", msg)); //$NON-NLS-1$ - } - - public void testPidKeyword() { - LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, - "123", "", "", "", "", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "sample message"); //$NON-NLS-1$ - assertEquals(false, search("123", msg)); //$NON-NLS-1$ - assertEquals(true, search("pid:123", msg)); //$NON-NLS-1$ - } - - public void testAppNameKeyword() { - LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "dalvik", "", "", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "sample message"); //$NON-NLS-1$ - assertEquals(false, search("dalv.*", msg)); //$NON-NLS-1$ - assertEquals(true, search("app:dal.*k", msg)); //$NON-NLS-1$ - } - - public void testCaseSensitivity() { - LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, - "", "", "", "", "", - "Sample message"); - - // if regex has an upper case character, it should be - // treated as a case sensitive search - assertEquals(false, search("Message", msg)); - - // if regex is all lower case, then it should be a - // case insensitive search - assertEquals(true, search("sample", msg)); - } - - /** - * Helper method: search if the query string matches the message. - * @param query words to search for - * @param message text to search in - * @return true if the encoded query is present in message - */ - private boolean search(String query, LogCatMessage message) { - List<LogCatFilter> filters = LogCatFilter.fromString(query, - LogLevel.VERBOSE); - - /* all filters have to match for the query to match */ - for (LogCatFilter f : filters) { - if (!f.matches(message)) { - return false; - } - } - return true; - } -} diff --git a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatMessageParserTest.java b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatMessageParserTest.java deleted file mode 100644 index dfde250..0000000 --- a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatMessageParserTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.ddmuilib.logcat; - -import com.android.ddmlib.Log.LogLevel; - -import java.util.List; - -import junit.framework.TestCase; - -/** - * Unit tests for {@link LogCatMessageParser}. - */ -public final class LogCatMessageParserTest extends TestCase { - private List<LogCatMessage> mParsedMessages; - - /** A list of messages generated with the following code: - * <pre> - * {@code - * Log.d("dtag", "debug message"); - * Log.e("etag", "error message"); - * Log.i("itag", "info message"); - * Log.v("vtag", "verbose message"); - * Log.w("wtag", "warning message"); - * Log.wtf("wtftag", "wtf message"); - * Log.d("dtag", "debug message"); - * } - * </pre> - * Note: On Android 2.3, Log.wtf doesn't really generate the message. - * It only produces the message header, but swallows the message tag. - * This string has been modified to include the message. - */ - private static final String[] MESSAGES = new String[] { - "[ 08-11 19:11:07.132 495:0x1ef D/dtag ]", //$NON-NLS-1$ - "debug message", //$NON-NLS-1$ - "[ 08-11 19:11:07.132 495: 234 E/etag ]", //$NON-NLS-1$ - "error message", //$NON-NLS-1$ - "[ 08-11 19:11:07.132 495:0x1ef I/itag ]", //$NON-NLS-1$ - "info message", //$NON-NLS-1$ - "[ 08-11 19:11:07.132 495:0x1ef V/vtag ]", //$NON-NLS-1$ - "verbose message", //$NON-NLS-1$ - "[ 08-11 19:11:07.132 495:0x1ef W/wtag ]", //$NON-NLS-1$ - "warning message", //$NON-NLS-1$ - "[ 08-11 19:11:07.132 495:0x1ef F/wtftag ]", //$NON-NLS-1$ - "wtf message", //$NON-NLS-1$ - "[ 08-11 21:15:35.7524 540:0x21c D/dtag ]", //$NON-NLS-1$ - "debug message", //$NON-NLS-1$ - }; - - @Override - protected void setUp() throws Exception { - LogCatMessageParser parser = new LogCatMessageParser(); - mParsedMessages = parser.processLogLines(MESSAGES, new LogCatPidToNameMapper(null)); - } - - /** Check that the correct number of messages are received. */ - public void testMessageCount() { - assertEquals(7, mParsedMessages.size()); - } - - /** Check the log level in a few of the parsed messages. */ - public void testLogLevel() { - assertEquals(mParsedMessages.get(0).getLogLevel(), LogLevel.DEBUG); - assertEquals(mParsedMessages.get(5).getLogLevel(), LogLevel.ASSERT); - } - - /** Check the parsed tag. */ - public void testTag() { - assertEquals(mParsedMessages.get(1).getTag(), "etag"); //$NON-NLS-1$ - } - - /** Check the time field. */ - public void testTime() { - assertEquals(mParsedMessages.get(6).getTime(), "08-11 21:15:35.7524"); //$NON-NLS-1$ - } - - /** Check the message field. */ - public void testMessage() { - assertEquals(mParsedMessages.get(2).getMessage(), MESSAGES[5]); - } - - public void testTid() { - assertEquals(mParsedMessages.get(0).getTid(), Integer.toString(0x1ef)); - assertEquals(mParsedMessages.get(1).getTid(), "234"); - } -} diff --git a/device_validator/dvlib/Android.mk b/device_validator/dvlib/Android.mk index 8db8260..8d07043 100644 --- a/device_validator/dvlib/Android.mk +++ b/device_validator/dvlib/Android.mk @@ -15,27 +15,20 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java) +# The dvlib code has moved to tools/base/dvlib. +# The rule below uses the prebuilt dvlib.jar. +# +# If you want to run the tests, cd to tools/base/dvlib +# and run ./gradlew :dvlib:test + LOCAL_JAVA_RESOURCE_DIRS := src/main/resources LOCAL_MODULE := dvlib LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_JAVA_LIBRARY) - - -# Build tests +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + ../../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src/test/java) -LOCAL_JAVA_RESOURCE_DIRS := src/test/resources - -LOCAL_MODULE := dvlib-tests -LOCAL_MODULE_TAGS := optional +include $(BUILD_HOST_PREBUILT) -LOCAL_JAVA_LIBRARIES := \ - dvlib \ - junit -include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java b/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java deleted file mode 100644 index b02471b..0000000 --- a/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.dvlib; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; - -import javax.xml.XMLConstants; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; -import javax.xml.validation.Validator; - -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.helpers.DefaultHandler; - -public class DeviceSchema { - - public static final String NS_DEVICES_XSD = "http://schemas.android.com/sdk/devices/1"; - - /** - * The "devices" element is the root element of this schema. - * - * It must contain one or more "device" elements that each define the - * hardware, software, and states for a given device. - */ - public static final String NODE_DEVICES = "devices"; - - /** - * A "device" element contains a "hardware" element, a "software" element - * for each API version it supports, and a "state" element for each possible - * state the device could be in. - */ - public static final String NODE_DEVICE = "device"; - - /** - * The "hardware" element contains all of the hardware information for a - * given device. - */ - public static final String NODE_HARDWARE = "hardware"; - - /** - * The "software" element contains all of the software information for an - * API version of the given device. - */ - public static final String NODE_SOFTWARE = "software"; - - /** - * The "state" element contains all of the parameters for a given state of - * the device. It's also capable of redefining hardware configurations if - * they change based on state. - */ - public static final String NODE_STATE = "state"; - - public static final String NODE_KEYBOARD = "keyboard"; - - public static final String NODE_TOUCH = "touch"; - - public static final String NODE_GL_EXTENSIONS = "gl-extensions"; - - public static final String NODE_GL_VERSION = "gl-version"; - - public static final String NODE_NETWORKING = "networking"; - - public static final String NODE_REMOVABLE_STORAGE = "removable-storage"; - - public static final String NODE_FLASH = "flash"; - - public static final String NODE_LIVE_WALLPAPER_SUPPORT = "live-wallpaper-support"; - - public static final String NODE_STATUS_BAR = "status-bar"; - - public static final String NODE_BUTTONS = "buttons"; - - public static final String NODE_CAMERA = "camera"; - - public static final String NODE_LOCATION = "location"; - - public static final String NODE_GPU = "gpu"; - - public static final String NODE_DOCK = "dock"; - - public static final String NODE_YDPI = "ydpi"; - - public static final String NODE_POWER_TYPE= "power-type"; - - public static final String NODE_Y_DIMENSION = "y-dimension"; - - public static final String NODE_SCREEN_RATIO = "screen-ratio"; - - public static final String NODE_NAV_STATE = "nav-state"; - - public static final String NODE_MIC = "mic"; - - public static final String NODE_RAM = "ram"; - - public static final String NODE_XDPI = "xdpi"; - - public static final String NODE_DIMENSIONS = "dimensions"; - - public static final String NODE_ABI = "abi"; - - public static final String NODE_MECHANISM = "mechanism"; - - public static final String NODE_MULTITOUCH = "multitouch"; - - public static final String NODE_NAV = "nav"; - - public static final String NODE_PIXEL_DENSITY = "pixel-density"; - - public static final String NODE_SCREEN_ORIENTATION = "screen-orientation"; - - public static final String NODE_AUTOFOCUS = "autofocus"; - - public static final String NODE_SCREEN_SIZE = "screen-size"; - - public static final String NODE_DESCRIPTION = "description"; - - public static final String NODE_BLUETOOTH_PROFILES = "bluetooth-profiles"; - - public static final String NODE_SCREEN = "screen"; - - public static final String NODE_SENSORS = "sensors"; - - public static final String NODE_DIAGONAL_LENGTH = "diagonal-length"; - - public static final String NODE_SCREEN_TYPE = "screen-type"; - - public static final String NODE_KEYBOARD_STATE = "keyboard-state"; - - public static final String NODE_X_DIMENSION = "x-dimension"; - - public static final String NODE_CPU = "cpu"; - - public static final String NODE_INTERNAL_STORAGE = "internal-storage"; - - public static final String NODE_META = "meta"; - - public static final String NODE_ICONS = "icons"; - - public static final String NODE_SIXTY_FOUR = "sixty-four"; - - public static final String NODE_SIXTEEN = "sixteen"; - - public static final String NODE_FRAME = "frame"; - - public static final String NODE_PATH = "path"; - - public static final String NODE_PORTRAIT_X_OFFSET = "portrait-x-offset"; - - public static final String NODE_PORTRAIT_Y_OFFSET = "portrait-y-offset"; - - public static final String NODE_LANDSCAPE_X_OFFSET = "landscape-x-offset"; - - public static final String NODE_LANDSCAPE_Y_OFFSET = "landscape-y-offset"; - - public static final String NODE_NAME = "name"; - - public static final String NODE_API_LEVEL = "api-level"; - - public static final String NODE_MANUFACTURER = "manufacturer"; - - public static final String ATTR_DEFAULT = "default"; - - public static final String ATTR_UNIT = "unit"; - - public static final String ATTR_NAME = "name"; - - /** - * Validates the input stream. - * - * @param deviceXml - * The XML InputStream to validate. - * @param out - * The OutputStream for error messages. - * @param parent - * The parent directory of the input stream. - * @return Whether the given input constitutes a valid devices file. - */ - public static boolean validate(InputStream deviceXml, OutputStream out, File parent) { - Schema s; - SAXParserFactory factory = SAXParserFactory.newInstance(); - PrintWriter writer = new PrintWriter(out); - try { - s = DeviceSchema.getSchema(); - factory.setValidating(false); - factory.setNamespaceAware(true); - factory.setSchema(s); - ValidationHandler validator = new ValidationHandler(parent, writer); - SAXParser parser = factory.newSAXParser(); - parser.parse(deviceXml, validator); - return validator.isValidDevicesFile(); - } catch (SAXException e) { - writer.println(e.getMessage()); - return false; - } catch (ParserConfigurationException e) { - writer.println("Error creating SAX parser:"); - writer.println(e.getMessage()); - return false; - } catch (IOException e) { - writer.println("Error reading file stream:"); - writer.println(e.getMessage()); - return false; - } finally { - writer.flush(); - } - } - - /** - * Helper to get an input stream of the device config XML schema. - */ - public static InputStream getXsdStream() { - return DeviceSchema.class.getResourceAsStream("devices.xsd"); //$NON-NLS-1$ - } - - /** Helper method that returns a {@link Validator} for our XSD */ - public static Schema getSchema() throws SAXException { - InputStream xsdStream = getXsdStream(); - SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - Schema schema = factory.newSchema(new StreamSource(xsdStream)); - return schema; - } - - /** - * A DefaultHandler that parses only to validate the XML is actually a valid - * devices config, since validation can't be entirely encoded in the devices - * schema. - */ - private static class ValidationHandler extends DefaultHandler { - private boolean mValidDevicesFile = true; - private boolean mDefaultSeen = false; - private String mDeviceName; - private final File mDirectory; - private final PrintWriter mWriter; - private final StringBuilder mStringAccumulator = new StringBuilder(); - - public ValidationHandler(File directory, PrintWriter writer) { - mDirectory = directory; // Possibly null - mWriter = writer; - } - - @Override - public void startElement(String uri, String localName, String name, Attributes attributes) - throws SAXException { - if (NODE_DEVICE.equals(localName)) { - // Reset for a new device - mDefaultSeen = false; - } else if (NODE_STATE.equals(localName)) { - // Check if the state is set to be a default state - String val = attributes.getValue(ATTR_DEFAULT); - if (val != null && ("1".equals(val) || Boolean.parseBoolean(val))) { - /* - * If it is and we already have a default state for this - * device, then the device configuration is invalid. - * Otherwise, set that we've seen a default state for this - * device and continue - */ - - if (mDefaultSeen) { - validationError("More than one default state for device " + mDeviceName); - } else { - mDefaultSeen = true; - } - } - } - mStringAccumulator.setLength(0); - } - - @Override - public void characters(char[] ch, int start, int length) { - mStringAccumulator.append(ch, start, length); - } - - @Override - public void endElement(String uri, String localName, String name) throws SAXException { - // If this is the end of a device node, make sure we have at least - // one default state - if (NODE_DEVICE.equals(localName) && !mDefaultSeen) { - validationError("No default state for device " + mDeviceName); - } else if (NODE_NAME.equals(localName)) { - mDeviceName = mStringAccumulator.toString().trim(); - } else if (NODE_PATH.equals(localName) || NODE_SIXTY_FOUR.equals(localName) - || NODE_SIXTEEN.equals(localName)) { - if (mDirectory == null) { - // There is no given parent directory, so this is not a - // valid devices file - validationError("No parent directory given, but relative paths exist."); - return; - } - // This is going to break on any files that end with a space, - // but that should be an incredibly rare corner case. - String relativePath = mStringAccumulator.toString().trim(); - File f = new File(mDirectory, relativePath); - if (f == null || !f.isFile()) { - validationError(relativePath + " is not a valid path."); - return; - } - String fileName = f.getName(); - int extensionStart = fileName.lastIndexOf("."); - if (extensionStart == -1 || !fileName.substring(extensionStart + 1).equals("png")) { - validationError(relativePath + " is not a valid file type."); - } - } - } - - @Override - public void error(SAXParseException e) { - validationError(e.getMessage()); - } - - @Override - public void fatalError(SAXParseException e) { - validationError(e.getMessage()); - } - - public boolean isValidDevicesFile() { - return mValidDevicesFile; - } - - private void validationError(String reason) { - mWriter.println("Error: " + reason); - mValidDevicesFile = false; - } - - } -} diff --git a/device_validator/dvlib/src/main/resources/com/android/dvlib/devices.xsd b/device_validator/dvlib/src/main/resources/com/android/dvlib/devices.xsd deleted file mode 100644 index bfa915f..0000000 --- a/device_validator/dvlib/src/main/resources/com/android/dvlib/devices.xsd +++ /dev/null @@ -1,892 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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. ---> - -<xsd:schema - targetNamespace="http://schemas.android.com/sdk/devices/1" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:c="http://schemas.android.com/sdk/devices/1" - elementFormDefault="qualified" - attributeFormDefault="unqualified" - version="1"> - - <xsd:element name="devices" type="c:devicesType" /> - - <xsd:complexType name="devicesType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - The "devices" element is the root element of this schema. - - It must contain one or more "device" elements that each define the configurations - and states available for a given device. - </xsd:documentation> - </xsd:annotation> - <xsd:sequence> - <xsd:element name="device" minOccurs="1" maxOccurs="unbounded"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - A device element contains one hardware profile for a device, along with - 1 or more software profiles and 1 or more states. Each software profile - defines the supported software for a given API release, and each state - profile defines a different possible state of the device (screen in - portrait orientation, screen in landscape orientation with the keyboard - out, etc.) - </xsd:documentation> - </xsd:annotation> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="name" type= "xsd:token" /> - <xsd:element name="manufacturer" type= "xsd:token" /> - <xsd:element name="meta" type= "c:metaType" minOccurs="0" /> - <xsd:element name="hardware" type= "c:hardwareType" /> - <xsd:element name="software" type= "c:softwareType" - maxOccurs="unbounded" /> - <xsd:element name="state" type= "c:stateType" - maxOccurs="unbounded" /> - </xsd:sequence> - </xsd:complexType> - </xsd:element> - </xsd:sequence> - </xsd:complexType> - - <xsd:complexType name="hardwareType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - The hardwareType contains all of the hardware information for - a given device. This includes things like the GPU type, screen - size, mic presence, etc. - </xsd:documentation> - </xsd:annotation> - <xsd:sequence> - <xsd:element name="screen" type= "c:screenType" /> - <xsd:element name="networking" type= "c:networkingType" /> - <xsd:element name="sensors" type= "c:sensorsType" /> - <xsd:element name="mic" type= "c:micType" /> - <xsd:element name="camera" type= "c:cameraType" - minOccurs="0" maxOccurs="unbounded" /> - <xsd:element name="keyboard" type= "c:keyboardType" /> - <xsd:element name="nav" type= "c:navType" /> - <xsd:element name="ram" type= "c:ramType" /> - <xsd:element name="buttons" type= "c:buttonsType" /> - <xsd:element name="internal-storage" type= "c:internalStorageType" /> - <xsd:element name="removable-storage" type= "c:removableStorageType" /> - <xsd:element name="cpu" type= "c:cpuType" /> - <xsd:element name="gpu" type= "c:gpuType" /> - <xsd:element name="abi" type= "c:abiType" /> - <xsd:element name="dock" type= "c:dockType" /> - <xsd:element name="power-type" type= "c:powerType" /> - </xsd:sequence> - </xsd:complexType> - - <xsd:complexType name="softwareType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - The softwareType contains all of the device's software - information for a given API version. This includes things like - live wallpaper support, OpenGL version, etc. - </xsd:documentation> - </xsd:annotation> - <xsd:sequence> - <xsd:element name="api-level"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies which API version(s) this this element is - defining. This can in the form of a single number - or a range of low to high, separated with a dash and - with either limit missing. The default lower limit is - one, and the default upper limit is unbounded. - The following are valid: - 10 - 7-10 - -10 - 7- - - - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:pattern value="[\d]*-[\d]*|[\d]+" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - <xsd:element name="live-wallpaper-support" type="xsd:boolean"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies whether the device supports live wallpapers. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - - <xsd:element name="bluetooth-profiles"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies all of the available Bluetooth profiles. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:list> - <xsd:simpleType> - <xsd:restriction base="xsd:NMTOKEN"> - <xsd:enumeration value="A2DP" /> - <xsd:enumeration value="ATT" /> - <xsd:enumeration value="AVRCP" /> - <xsd:enumeration value="AVDTP" /> - <xsd:enumeration value="BIP" /> - <xsd:enumeration value="BPP" /> - <xsd:enumeration value="CIP" /> - <xsd:enumeration value="CTP" /> - <xsd:enumeration value="DIP" /> - <xsd:enumeration value="DUN" /> - <xsd:enumeration value="FAX" /> - <xsd:enumeration value="FTP" /> - <xsd:enumeration value="GAVDP" /> - <xsd:enumeration value="GAP" /> - <xsd:enumeration value="GATT" /> - <xsd:enumeration value="GOEP" /> - <xsd:enumeration value="HCRP" /> - <xsd:enumeration value="HDP" /> - <xsd:enumeration value="HFP" /> - <xsd:enumeration value="HID" /> - <xsd:enumeration value="HSP" /> - <xsd:enumeration value="ICP" /> - <xsd:enumeration value="LAP" /> - <xsd:enumeration value="MAP" /> - <xsd:enumeration value="OPP" /> - <xsd:enumeration value="PAN" /> - <xsd:enumeration value="PBA" /> - <xsd:enumeration value="PBAP" /> - <xsd:enumeration value="SPP" /> - <xsd:enumeration value="SDAP" /> - <xsd:enumeration value="SAP" /> - <xsd:enumeration value="SIM" /> - <xsd:enumeration value="rSAP" /> - <xsd:enumeration value="SYNCH" /> - <xsd:enumeration value="VDP" /> - <xsd:enumeration value="WAPB" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:list> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="gl-version"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the OpenGL version supported for this release. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:decimal"> - <xsd:pattern value="[0-9]\.[0-9]" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="gl-extensions"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies all of the supported OpenGL extensions for - this release. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:list itemType="xsd:NMTOKEN" /> - </xsd:simpleType> - </xsd:element> - <xsd:element name="status-bar" type="xsd:boolean"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies whether the device has a status bar in this - software configuration. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - </xsd:sequence> - </xsd:complexType> - - <xsd:complexType name="stateType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - The stateType contains the information for a given state of - of the device. States include things like portrait mode, - landscape with the keyboard exposed, etc. States can also - modify the hardware attributes of a device. For instance, if - sliding out the keyboard increased the available screen - real estate, you can define a new screenType to override the - default one defined in the device's hardwareType. - </xsd:documentation> - </xsd:annotation> - <xsd:sequence> - <xsd:element name="description" type="xsd:token"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - A description of the defined state. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - - <xsd:element name="screen-orientation"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Defines the orientation of the screen. Use square if - the device's screen has equal height and width, - otherwise use landscape or portrait. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="port" /> - <xsd:enumeration value="land" /> - <xsd:enumeration value="square" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="keyboard-state"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Defines the state of the keyboard. If the device has no - keyboard use keysoft, otherwise use keysexposed or keyshidden. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="keyssoft" /> - <xsd:enumeration value="keyshidden" /> - <xsd:enumeration value="keysexposed" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - <xsd:element name="nav-state"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Defines the state of the primary non-touchscreen - navigation hardware on the devices. If the device - doesn't have non-touchscreen navigation hardware use - nonav, otherwise use navexposed or navhidden. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="nonav" /> - <xsd:enumeration value="navhidden" /> - <xsd:enumeration value="navexposed" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - <xsd:element name="screen" type="c:screenType" minOccurs="0" /> - <xsd:element name="networking" type="c:networkingType" - minOccurs="0" /> - <xsd:element name="sensors" type="c:sensorsType" minOccurs="0" /> - <xsd:element name="mic" type="c:micType" minOccurs="0" /> - <xsd:element name="camera" type="c:cameraType" - minOccurs="0" maxOccurs="unbounded" /> - <xsd:element name="keyboard" type="c:keyboardType" minOccurs="0" /> - <xsd:element name="nav" type="c:navType" minOccurs="0" /> - <xsd:element name="ram" type="c:ramType" minOccurs="0" /> - <xsd:element name="buttons" type="c:buttonsType" minOccurs="0" /> - <xsd:element name="internal-storage" type="c:internalStorageType" - minOccurs="0" /> - <xsd:element name="removable-storage" type="c:removableStorageType" - minOccurs="0" /> - <xsd:element name="cpu" type="c:cpuType" minOccurs="0" /> - <xsd:element name="gpu" type="c:gpuType" minOccurs="0" /> - <xsd:element name="abi" type="c:abiType" minOccurs="0" /> - <xsd:element name="dock" type="c:dockType" minOccurs="0" /> - <xsd:element name="power-type" type="c:powerType" - minOccurs="0" /> - </xsd:sequence> - <xsd:attribute name="name" use="required" type="xsd:token" /> - <xsd:attribute name="default" use="optional" type="xsd:boolean" /> - </xsd:complexType> - - <xsd:complexType name="metaType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Details where more device information can be found, such as - icons and frame images. - </xsd:documentation> - </xsd:annotation> - <xsd:sequence> - <xsd:element name="icons" minOccurs="0"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Contains the relative paths to the icon files for this - device. - </xsd:documentation> - </xsd:annotation> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="sixty-four" type="xsd:normalizedString"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Relative path for the 64x64 icon. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - <xsd:element name="sixteen" type="xsd:normalizedString" - minOccurs="0"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Relative path for the 16x16 icon. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - </xsd:sequence> - </xsd:complexType> - </xsd:element> - <xsd:element name="frame" minOccurs="0"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Contains information about the frame for the device. - </xsd:documentation> - </xsd:annotation> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="path" - type="xsd:normalizedString"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - The relative path to the emulator frame for - the device. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - <xsd:element name="portrait-x-offset" - type="xsd:nonNegativeInteger"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - The offset for the frame in the x direction, - in portrait mode. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - <xsd:element name="portrait-y-offset" - type="xsd:nonNegativeInteger"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - The offset for the frame in the y direction, - in portrait mode. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - <xsd:element name="landscape-x-offset" - type="xsd:nonNegativeInteger"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - The offset for the frame in the x direction, - in landscape mode. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - <xsd:element name="landscape-y-offset" - type="xsd:nonNegativeInteger"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - The offset for the frame in the y direction, - in landscape mode. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - </xsd:sequence> - </xsd:complexType> - </xsd:element> - </xsd:sequence> - </xsd:complexType> - - <xsd:complexType name="screenType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Contains the specifications for the device's screen. - </xsd:documentation> - </xsd:annotation> - <xsd:sequence> - <xsd:element name="screen-size"> - <xsd:simpleType> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the class of the screen. - </xsd:documentation> - </xsd:annotation> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="small" /> - <xsd:enumeration value="normal" /> - <xsd:enumeration value="large" /> - <xsd:enumeration value="xlarge" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="diagonal-length"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the diagonal length of the screen in inches. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:decimal"> - <!-- Negative lengths are not valid --> - <xsd:minInclusive value="0" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="pixel-density"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the screen density of the device. The - medium density of traditional HVGA screens (mdpi) - is defined to be approximately 160dpi; low density - (ldpi) is 120, and high density (hdpi) is 240. There - is thus a 4:3 scaling factor between each density, - so a 9x9 bitmap in ldpi would be 12x12 in mdpi and - 16x16 in hdpi. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="ldpi" /> - <xsd:enumeration value="mdpi" /> - <xsd:enumeration value="tvdpi" /> - <xsd:enumeration value="hdpi" /> - <xsd:enumeration value="xhdpi" /> - <xsd:enumeration value="xxhdpi" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="screen-ratio"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies whether the configuration is for a taller or - wider than traditional screen. This is based purely on - the aspect ratio of the screen: QVGA, HVGA, and VGA are - notlong; WQVGA, WVGA, FWVGA are long. Note that long may - mean either wide or tall, depending on the current - orientation. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="notlong" /> - <xsd:enumeration value="long" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="dimensions"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the device screen resolution in pixels. - </xsd:documentation> - </xsd:annotation> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="x-dimension"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the x-dimension's resolution in - pixels. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:positiveInteger" /> - </xsd:simpleType> - </xsd:element> - <xsd:element name="y-dimension"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the y-dimension's resolution in - pixels. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:positiveInteger" /> - </xsd:simpleType> - </xsd:element> - </xsd:sequence> - </xsd:complexType> - </xsd:element> - - <xsd:element name="xdpi"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the actual density in X of the device screen. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:decimal"> - <!-- Negative DPIs are not valid --> - <xsd:minInclusive value="0" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="ydpi"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the actual density in Y of the device screen. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:decimal"> - <!-- Negative DPIs are not valid --> - <xsd:minInclusive value="0" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="touch"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the touch properties of the device. - </xsd:documentation> - </xsd:annotation> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="multitouch"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the multitouch capabilities of the - device. This can be none if multitouch is - not supported, basic if the device can track - only basic two finger gestures, distinct if - the device can track two or more fingers - simultaneously, or jazz-hands if the device - can track 5 or more fingers simultaneously. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="none" /> - <xsd:enumeration value="basic" /> - <xsd:enumeration value="distinct" /> - <xsd:enumeration value="jazz-hands" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="mechanism"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the mechanism the device was - created for. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="notouch" /> - <xsd:enumeration value="stylus" /> - <xsd:enumeration value="finger" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="screen-type"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the type of touch screen on the - device. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="notouch" /> - <xsd:enumeration value="capacitive" /> - <xsd:enumeration value="resistive" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - </xsd:sequence> - </xsd:complexType> - </xsd:element> - - </xsd:sequence> - </xsd:complexType> - - <xsd:simpleType name="networkingType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the available networking hardware. - </xsd:documentation> - </xsd:annotation> - <xsd:list> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="NFC" /> - <xsd:enumeration value="Bluetooth" /> - <xsd:enumeration value="Wifi" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:list> - </xsd:simpleType> - - <xsd:simpleType name="sensorsType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the available sensors. - </xsd:documentation> - </xsd:annotation> - <xsd:list> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="Accelerometer" /> - <xsd:enumeration value="Barometer" /> - <xsd:enumeration value="Compass" /> - <xsd:enumeration value="GPS" /> - <xsd:enumeration value="Gyroscope" /> - <xsd:enumeration value="LightSensor" /> - <xsd:enumeration value="ProximitySensor" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:list> - </xsd:simpleType> - - <xsd:simpleType name="micType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies whether the device has a mic or not. - </xsd:documentation> - </xsd:annotation> - <xsd:restriction base="xsd:boolean" /> - </xsd:simpleType> - - <xsd:complexType name="cameraType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the attributes of the camera. - </xsd:documentation> - </xsd:annotation> - <xsd:sequence> - <xsd:element name="location"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the location of the camera. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="front" /> - <xsd:enumeration value="back" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:element> - - <xsd:element name="autofocus" type="xsd:boolean"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies whether the camera can autofocus - </xsd:documentation> - </xsd:annotation> - </xsd:element> - - <xsd:element name="flash" type="xsd:boolean"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies whether the camera has flash. - </xsd:documentation> - </xsd:annotation> - </xsd:element> - </xsd:sequence> - </xsd:complexType> - - <xsd:simpleType name="keyboardType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the type of keyboard on the device. - </xsd:documentation> - </xsd:annotation> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="qwerty" /> - <xsd:enumeration value="12key" /> - <xsd:enumeration value="nokeys" /> - </xsd:restriction> - </xsd:simpleType> - - <xsd:simpleType name="navType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the primary non-touchscreen navigation - hardware on the device. - </xsd:documentation> - </xsd:annotation> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="dpad" /> - <xsd:enumeration value="trackball" /> - <xsd:enumeration value="wheel" /> - <xsd:enumeration value="nonav" /> - </xsd:restriction> - </xsd:simpleType> - - <xsd:complexType name="ramType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the amount of RAM on the device in the unit provided. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleContent> - <xsd:extension base="xsd:positiveInteger"> - <xsd:attribute name="unit" type="c:storageUnitType" use="required" /> - </xsd:extension> - </xsd:simpleContent> - </xsd:complexType> - - <xsd:simpleType name="buttonsType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies whether the device has physical (hard) buttons - (Home, Search, etc.), or uses soft buttons. - </xsd:documentation> - </xsd:annotation> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="hard" /> - <xsd:enumeration value="soft" /> - </xsd:restriction> - </xsd:simpleType> - - <xsd:complexType name="internalStorageType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - A list specifying the sizes of internal storage in - the device, in the storage size unit provided. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleContent> - <xsd:extension base="c:storageListType"> - <xsd:attribute name="unit" type="c:storageUnitType" - use="required" /> - </xsd:extension> - </xsd:simpleContent> - </xsd:complexType> - - <xsd:complexType name="removableStorageType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the range of available removable storage sizes - in the unit provided. A positive value indicates the device is - available with that storage size included while a zero value - indicates an empty storage slot. - </xsd:documentation> - </xsd:annotation> - <xsd:simpleContent> - <xsd:extension base="c:storageListType"> - <xsd:attribute name="unit" type="c:storageUnitType" - use="required" /> - </xsd:extension> - </xsd:simpleContent> - </xsd:complexType> - - <xsd:simpleType name="storageListType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Defines a list for storage configurations such as internal or - removable storage. A positive value indicates the the device - has a storage unit of that size, while a zero value indicates - there is an empty location for a storage unit (such as an empty - SD card slot). - </xsd:documentation> - </xsd:annotation> - <xsd:list> - <xsd:simpleType> - <xsd:restriction base="xsd:nonNegativeInteger" /> - </xsd:simpleType> - </xsd:list> - </xsd:simpleType> - <xsd:simpleType name="gpuType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the device's GPU. - </xsd:documentation> - </xsd:annotation> - <xsd:restriction base="xsd:token"> - <xsd:minLength value="1" /> - </xsd:restriction> - </xsd:simpleType> - - <xsd:simpleType name="cpuType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the device's CPU. - </xsd:documentation> - </xsd:annotation> - <xsd:restriction base="xsd:token"> - <xsd:minLength value="1" /> - </xsd:restriction> - </xsd:simpleType> - - <xsd:simpleType name="abiType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies which ABIs the device conforms to. - </xsd:documentation> - </xsd:annotation> - <xsd:list> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="armeabi" /> - <xsd:enumeration value="armeabi-v7a" /> - <xsd:enumeration value="x86" /> - <xsd:enumeration value="mips" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:list> - </xsd:simpleType> - - <xsd:simpleType name="dockType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the official docks available for the device. - </xsd:documentation> - </xsd:annotation> - <xsd:list> - <xsd:simpleType> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="desk" /> - <xsd:enumeration value="television" /> - <xsd:enumeration value="car" /> - </xsd:restriction> - </xsd:simpleType> - </xsd:list> - </xsd:simpleType> - - <xsd:simpleType name="powerType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies whether the device is plugged in. - </xsd:documentation> - </xsd:annotation> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="plugged-in" /> - <xsd:enumeration value="battery" /> - </xsd:restriction> - </xsd:simpleType> - - <xsd:simpleType name="storageUnitType"> - <xsd:annotation> - <xsd:documentation xml:lang="en"> - Specifies the unit of storage. This can be MiB, GiB, etc. - </xsd:documentation> - </xsd:annotation> - <xsd:restriction base="xsd:token"> - <xsd:enumeration value="B" /> - <xsd:enumeration value="KiB" /> - <xsd:enumeration value="MiB" /> - <xsd:enumeration value="GiB" /> - <xsd:enumeration value="TiB" /> - </xsd:restriction> - </xsd:simpleType> - -</xsd:schema> diff --git a/device_validator/dvlib/src/test/java/com/android/dvlib/DeviceSchemaTest.java b/device_validator/dvlib/src/test/java/com/android/dvlib/DeviceSchemaTest.java deleted file mode 100644 index de70ef3..0000000 --- a/device_validator/dvlib/src/test/java/com/android/dvlib/DeviceSchemaTest.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * 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.dvlib; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.StringWriter; -import java.util.HashMap; -import java.util.Map; -import java.util.Stack; - -import javax.xml.XMLConstants; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import junit.framework.TestCase; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.helpers.DefaultHandler; - -public class DeviceSchemaTest extends TestCase { - - private void checkFailure(Map<String, String> replacements, String regex) throws Exception { - // Generate XML stream with replacements - InputStream xmlStream = getReplacedStream(replacements); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - assertFalse( - "Validation Assertion Failed, XML failed to validate when it was expected to pass\n", - DeviceSchema.validate(xmlStream, baos, null)); - assertTrue(String.format("Regex Assertion Failed:\nExpected: %s\nActual: %s\n", regex, baos - .toString().trim()), baos.toString().trim().matches(regex)); - } - - private void checkFailure(String resource, String regex) throws Exception { - InputStream xml = DeviceSchemaTest.class.getResourceAsStream(resource); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - assertFalse("Validation Assertion Failed, XML validated when it was expected to fail\n", - DeviceSchema.validate(xml, baos, null)); - assertTrue(String.format("Regex Assertion Failed:\nExpected: %s\nActual: %s\n", regex, baos - .toString().trim()), baos.toString().trim().matches(regex)); - } - - private void checkSuccess(Map<String, String> replacements) throws Exception { - InputStream xmlStream = getReplacedStream(replacements); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - assertTrue(DeviceSchema.validate(xmlStream, baos, null)); - assertTrue(baos.toString().trim().matches("")); - } - - public static InputStream getReplacedStream(Map<String, String> replacements) throws Exception { - InputStream xml = DeviceSchema.class.getResourceAsStream("devices_minimal.xml"); - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware(true); - SAXParser parser = factory.newSAXParser(); - ReplacementHandler replacer = new ReplacementHandler(replacements); - parser.parse(xml, replacer); - Document doc = replacer.getGeneratedDocument(); - Transformer tf = TransformerFactory.newInstance().newTransformer(); - // Add indents so we're closer to user generated output - tf.setOutputProperty(OutputKeys.INDENT, "yes"); - DOMSource source = new DOMSource(doc); - StringWriter out = new StringWriter(); - StreamResult result = new StreamResult(out); - tf.transform(source, result); - return new ByteArrayInputStream(out.toString().getBytes("UTF-8")); - } - - public void testValidXml() throws Exception { - InputStream xml = DeviceSchemaTest.class.getResourceAsStream("devices.xml"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - boolean result = DeviceSchema.validate(xml, baos, null); - String output = baos.toString().trim(); - assertTrue( - String.format( - "Validation Assertion Failed, XML failed to validate when it was expected to pass\n%s\n",output), result); - assertTrue(String.format("Regex Assertion Failed\nExpected No Output\nActual: %s\n", baos - .toString().trim()), baos.toString().trim().matches("")); - } - - public void testNoHardware() throws Exception { - String regex = "Error: cvc-complex-type.2.4.a: Invalid content was found starting with " - + "element 'd:software'.*"; - checkFailure("devices_no_hardware.xml", regex); - } - - public void testNoSoftware() throws Exception { - String regex = "Error: cvc-complex-type.2.4.a: Invalid content was found starting with " - + "element 'd:state'.*"; - checkFailure("devices_no_software.xml", regex); - } - - public void testNoDefault() throws Exception { - String regex = "Error: No default state for device Galaxy Nexus.*"; - checkFailure("devices_no_default.xml", regex); - } - - public void testTooManyDefaults() throws Exception { - String regex = "Error: More than one default state for device Galaxy Nexus.*"; - checkFailure("devices_too_many_defaults.xml", regex); - } - - public void testNoStates() throws Exception { - String regex = "Error: cvc-complex-type.2.4.b: The content of element 'd:device' is not " - + "complete.*\nError: No default state for device Galaxy Nexus.*"; - checkFailure("devices_no_states.xml", regex); - } - - public void testBadMechanism() throws Exception { - Map<String, String> replacements = new HashMap<String, String>(); - replacements.put(DeviceSchema.NODE_MECHANISM, "fanger"); - checkFailure(replacements, "Error: cvc-enumeration-valid: Value 'fanger' is not " - + "facet-valid.*\nError: cvc-type.3.1.3: The value 'fanger' of element " - + "'d:mechanism' is not valid.*"); - } - - public void testNegativeXdpi() throws Exception { - Map<String, String> replacements = new HashMap<String, String>(); - replacements.put(DeviceSchema.NODE_XDPI, "-1.0"); - checkFailure(replacements, "Error: cvc-minInclusive-valid: Value '-1.0'.*\n" - + "Error: cvc-type.3.1.3: The value '-1.0' of element 'd:xdpi' is not valid.*"); - } - - public void testNegativeYdpi() throws Exception { - Map<String, String> replacements = new HashMap<String, String>(); - replacements.put(DeviceSchema.NODE_YDPI, "-1"); - checkFailure(replacements, "Error: cvc-minInclusive-valid: Value '-1'.*\n" - + "Error: cvc-type.3.1.3: The value '-1' of element 'd:ydpi' is not valid.*"); - - } - - public void testNegativeDiagonalLength() throws Exception { - Map<String, String> replacements = new HashMap<String, String>(); - replacements.put(DeviceSchema.NODE_DIAGONAL_LENGTH, "-1.0"); - - checkFailure(replacements, "Error: cvc-minInclusive-valid: Value '-1.0'.*\n" - + "Error: cvc-type.3.1.3: The value '-1.0' of element 'd:diagonal-length'.*"); - - } - - public void testInvalidOpenGLVersion() throws Exception { - Map<String, String> replacements = new HashMap<String, String>(); - replacements.put(DeviceSchema.NODE_GL_VERSION, "2"); - checkFailure(replacements, "Error: cvc-pattern-valid: Value '2' is not facet-valid.*\n" - + "Error: cvc-type.3.1.3: The value '2' of element 'd:gl-version' is not valid.*"); - } - - public void testEmptyOpenGLExtensions() throws Exception { - Map<String, String> replacements = new HashMap<String, String>(); - replacements.put(DeviceSchema.NODE_GL_EXTENSIONS, ""); - checkSuccess(replacements); - } - - public void testEmptySensors() throws Exception { - Map<String, String> replacements = new HashMap<String, String>(); - replacements.put(DeviceSchema.NODE_SENSORS, ""); - checkSuccess(replacements); - } - - public void testEmptyNetworking() throws Exception { - Map<String, String> replacements = new HashMap<String, String>(); - replacements.put(DeviceSchema.NODE_NETWORKING, ""); - checkSuccess(replacements); - } - - public void testEmptyCpu() throws Exception { - Map<String, String> replacements = new HashMap<String, String>(); - replacements.put(DeviceSchema.NODE_CPU, ""); - checkFailure(replacements, "Error: cvc-minLength-valid: Value '' with length = '0'.*\n" - + "Error: cvc-type.3.1.3: The value '' of element 'd:cpu' is not valid.*"); - } - - public void testEmptyGpu() throws Exception { - Map<String, String> replacements = new HashMap<String, String>(); - replacements.put(DeviceSchema.NODE_GPU, ""); - checkFailure(replacements, "Error: cvc-minLength-valid: Value '' with length = '0'.*\n" - + "Error: cvc-type.3.1.3: The value '' of element 'd:gpu' is not valid.*"); - } - - /** - * Reads in a valid devices XML file and if an element tag is in the - * replacements map, it replaces its text content with the corresponding - * value. Note this has no concept of namespaces or hierarchy, so it will - * replace the contents any and all elements with the specified tag name. - */ - private static class ReplacementHandler extends DefaultHandler { - private Element mCurrElement = null; - private Document mDocument; - private final Stack<Element> mElementStack = new Stack<Element>(); - private final Map<String, String> mPrefixes = new HashMap<String, String>(); - private final Map<String, String> mReplacements; - private final StringBuilder mStringAccumulator = new StringBuilder(); - - public ReplacementHandler(Map<String, String> replacements) { - mReplacements = replacements; - } - - @Override - public void startDocument() { - try { - mDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); - } catch (ParserConfigurationException e) { - fail(e.getMessage()); - } - } - - @Override - public void startElement(String uri, String localName, String name, Attributes attributes) { - Element element = mDocument.createElement(name); - for (int i = 0; i < attributes.getLength(); i++) { - element.setAttribute(attributes.getQName(i), attributes.getValue(i)); - } - for (String key : mPrefixes.keySet()) { - element.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + key, mPrefixes.get(key)); - } - mPrefixes.clear(); - if (mCurrElement != null) { - mElementStack.push(mCurrElement); - } - mCurrElement = element; - } - - @Override - public void startPrefixMapping(String prefix, String uri) throws SAXException { - mPrefixes.put(prefix, uri); - } - - @Override - public void characters(char[] ch, int start, int length) { - mStringAccumulator.append(ch, start, length); - } - - @Override - public void endElement(String uri, String localName, String name) throws SAXException { - if (mReplacements.containsKey(localName)) { - mCurrElement.appendChild(mDocument.createTextNode(mReplacements.get(localName))); - } else { - String content = mStringAccumulator.toString().trim(); - if (!content.isEmpty()) { - mCurrElement.appendChild(mDocument.createTextNode(content)); - } - } - - if (mElementStack.empty()) { - mDocument.appendChild(mCurrElement); - mCurrElement = null; - } else { - Element parent = mElementStack.pop(); - parent.appendChild(mCurrElement); - mCurrElement = parent; - } - mStringAccumulator.setLength(0); - } - - @Override - public void error(SAXParseException e) { - fail(e.getMessage()); - } - - @Override - public void fatalError(SAXParseException e) { - fail(e.getMessage()); - } - - public Document getGeneratedDocument() { - return mDocument; - } - - } -} diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml deleted file mode 100644 index 6662099..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml +++ /dev/null @@ -1,273 +0,0 @@ -<?xml version="1.0"?> -<d:devices - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:d="http://schemas.android.com/sdk/devices/1"> - - <d:device> - <d:name> - Galaxy Nexus - </d:name> - <d:manufacturer> - Samsung - </d:manufacturer> - <d:hardware> - <d:screen> - <d:screen-size>normal</d:screen-size> - <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> - <d:pixel-density>xhdpi</d:pixel-density> - <d:screen-ratio>long</d:screen-ratio> - <d:dimensions> - <d:x-dimension>720</d:x-dimension> - <d:y-dimension>1280</d:y-dimension> - </d:dimensions> - <d:xdpi>316</d:xdpi> - <d:ydpi>316</d:ydpi> - <d:touch> - <d:multitouch>jazz-hands</d:multitouch> - <d:mechanism>finger</d:mechanism> - <d:screen-type>capacitive</d:screen-type> - </d:touch> - </d:screen> - <d:networking> - Bluetooth - Wifi - NFC - </d:networking> - <d:sensors> - Accelerometer - Barometer - Gyroscope - Compass - GPS - ProximitySensor - </d:sensors> - <d:mic>true</d:mic> - <d:camera> - <d:location>front</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>false</d:flash> - </d:camera> - <d:camera> - <d:location>back</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>true</d:flash> - </d:camera> - <d:keyboard>nokeys</d:keyboard> - <d:nav>nonav</d:nav> - <d:ram unit="GiB">1</d:ram> - <d:buttons>soft</d:buttons> - <d:internal-storage unit="GiB">16</d:internal-storage> - <d:removable-storage unit="KiB"></d:removable-storage> - <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> - <d:gpu>PowerVR SGX540</d:gpu> - <d:abi> - armeabi - armeabi-v7a - </d:abi> - <!--dock (car, desk, tv, none)--> - <d:dock> - </d:dock> - <!-- plugged in (never, charge, always) --> - <d:power-type>battery</d:power-type> - </d:hardware> - <d:software> - <d:api-level>14-</d:api-level> - <d:live-wallpaper-support>true</d:live-wallpaper-support> - <d:bluetooth-profiles> - HSP - HFP - SPP - A2DP - AVRCP - OPP - PBAP - GAVDP - AVDTP - HID - HDP - PAN - </d:bluetooth-profiles> - <d:gl-version>2.0</d:gl-version> - <!-- - These can be gotten via - javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); - --> - <d:gl-extensions> - GL_EXT_discard_framebuffer - GL_EXT_multi_draw_arrays - GL_EXT_shader_texture_lod - GL_EXT_texture_format_BGRA8888 - GL_IMG_multisampled_render_to_texture - GL_IMG_program_binary - GL_IMG_read_format - GL_IMG_shader_binary - GL_IMG_texture_compression_pvrtc - GL_IMG_texture_format_BGRA8888 - GL_IMG_texture_npot - GL_OES_compressed_ETC1_RGB8_texture - GL_OES_depth_texture - GL_OES_depth24 - GL_OES_EGL_image - GL_OES_EGL_image_external - GL_OES_egl_sync - GL_OES_element_index_uint - GL_OES_fragment_precision_high - GL_OES_get_program_binary - GL_OES_mapbuffer - GL_OES_packed_depth_stencil - GL_OES_required_internalformat - GL_OES_rgb8_rgba8 - GL_OES_standard_derivatives - GL_OES_texture_float - GL_OES_texture_half_float - GL_OES_vertex_array_object - GL_OES_vertex_half_float - </d:gl-extensions> - <d:status-bar>true</d:status-bar> - </d:software> - <d:state name="Portrait" default="true"> - <d:description>The phone in portrait view</d:description> - <d:screen-orientation>port</d:screen-orientation> - <d:keyboard-state>keyssoft</d:keyboard-state> - <d:nav-state>nonav</d:nav-state> - </d:state> - <d:state name="Landscape"> - <d:description>The phone in landscape view</d:description> - <d:screen-orientation>land</d:screen-orientation> - <d:keyboard-state>keyssoft</d:keyboard-state> - <d:nav-state>nonav</d:nav-state> - </d:state> - </d:device> - <d:device> - <d:name>Droid</d:name> - <d:manufacturer>Motorola</d:manufacturer> - <d:hardware> - <d:screen> - <d:screen-size>normal</d:screen-size> - <d:diagonal-length>3.7</d:diagonal-length> - <d:pixel-density>hdpi</d:pixel-density> - <d:screen-ratio>long</d:screen-ratio> - <d:dimensions> - <d:x-dimension>480</d:x-dimension> - <d:y-dimension>854</d:y-dimension> - </d:dimensions> - <d:xdpi>265</d:xdpi> - <d:ydpi>265</d:ydpi> - <d:touch> - <d:multitouch>distinct</d:multitouch> - <d:mechanism>finger</d:mechanism> - <d:screen-type>capacitive</d:screen-type> - </d:touch> - </d:screen> - <d:networking> - Bluetooth - Wifi - NFC - </d:networking> - <d:sensors> - Accelerometer - Barometer - Compass - GPS - ProximitySensor - LightSensor - </d:sensors> - <d:mic>true</d:mic> - <d:camera> - <d:location>back</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>true</d:flash> - </d:camera> - <d:keyboard>qwerty</d:keyboard> - <d:nav>dpad</d:nav> - <d:ram unit="MiB">256</d:ram> - <d:buttons>hard</d:buttons> - <d:internal-storage unit="MiB">512</d:internal-storage> - <d:removable-storage unit="GiB">16</d:removable-storage> - <d:cpu>OMAP 3430</d:cpu> - <d:gpu>PowerVR SGX 53</d:gpu> - <d:abi> - armeabi - armeabi-v7a - </d:abi> - <d:dock> - car - desk - </d:dock> - <d:power-type>battery</d:power-type> - </d:hardware> - <d:software> - <d:api-level>5-8</d:api-level> - <d:live-wallpaper-support>false</d:live-wallpaper-support> - <d:bluetooth-profiles> - GAP - SPP - HSP - HFP - A2DP - AVRCP - SDAP - </d:bluetooth-profiles> - <d:gl-version>1.1</d:gl-version> - <!-- - These can be gotten via - javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); - --> - <d:gl-extensions> - GL_OES_byte_coordinates - GL_OES_fixed_point - GL_OES_single_precision - GL_OES_matrix_get - GL_OES_read_format - GL_OES_compressed_paletted_texture - GL_OES_point_sprite - GL_OES_point_size_array - GL_OES_matrix_palette - GL_OES_draw_texture - GL_OES_query_matrix - GL_OES_texture_env_crossbar - GL_OES_texture_mirrored_repeat - GL_OES_texture_cube_map - GL_OES_blend_subtract - GL_OES_blend_func_separate - GL_OES_blend_equation_separate - GL_OES_stencil_wrap - GL_OES_extended_matrix_palette - GL_OES_framebuffer_object - GL_OES_rgb8_rgba8 - GL_OES_depth24 - GL_OES_stencil8 - GL_OES_compressed_ETC1_RGB8_texture - GL_OES_mapbuffer - GL_OES_EGL_image - GL_EXT_multi_draw_arrays - GL_OES_required_internalformat - GL_IMG_read_format - GL_IMG_texture_compression_pvrtc - GL_IMG_texture_format_BGRA8888 - GL_EXT_texture_format_BGRA8888 - GL_IMG_texture_stream - GL_IMG_vertex_program - </d:gl-extensions> - <d:status-bar>true</d:status-bar> - </d:software> - <d:state name="Portrait" default="true"> - <d:description>The phone in portrait view</d:description> - <d:screen-orientation>port</d:screen-orientation> - <d:keyboard-state>keyshidden</d:keyboard-state> - <d:nav-state>navhidden</d:nav-state> - </d:state> - <d:state name="Landscape, closed"> - <d:description>The phone in landscape view with the keyboard closed</d:description> - <d:screen-orientation>land</d:screen-orientation> - <d:keyboard-state>keyshidden</d:keyboard-state> - <d:nav-state>navhidden</d:nav-state> - </d:state> - <d:state name="Landscape, open"> - <d:description>The phone in landscape view with the keyboard open</d:description> - <d:screen-orientation>land</d:screen-orientation> - <d:keyboard-state>keysexposed</d:keyboard-state> - <d:nav-state>navexposed</d:nav-state> - </d:state> - </d:device> -</d:devices> diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_minimal.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_minimal.xml deleted file mode 100644 index e063fd1..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_minimal.xml +++ /dev/null @@ -1,135 +0,0 @@ -<?xml version="1.0"?> -<d:devices - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:d="http://schemas.android.com/sdk/devices/1"> - - <d:device> - <d:name> - Galaxy Nexus - </d:name> - <d:manufacturer> - Samsung - </d:manufacturer> - <d:hardware> - <d:screen> - <d:screen-size>normal</d:screen-size> - <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> - <d:pixel-density>xhdpi</d:pixel-density> - <d:screen-ratio>long</d:screen-ratio> - <d:dimensions> - <d:x-dimension>720</d:x-dimension> - <d:y-dimension>1280</d:y-dimension> - </d:dimensions> - <d:xdpi>316</d:xdpi> - <d:ydpi>316</d:ydpi> - <d:touch> - <d:multitouch>jazz-hands</d:multitouch> - <d:mechanism>finger</d:mechanism> - <d:screen-type>capacitive</d:screen-type> - </d:touch> - </d:screen> - <d:networking> - Bluetooth - Wifi - NFC - </d:networking> - <d:sensors> - Accelerometer - Barometer - Gyroscope - Compass - GPS - ProximitySensor - </d:sensors> - <d:mic>true</d:mic> - <d:camera> - <d:location>front</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>false</d:flash> - </d:camera> - <d:camera> - <d:location>back</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>true</d:flash> - </d:camera> - <d:keyboard>nokeys</d:keyboard> - <d:nav>nonav</d:nav> - <d:ram unit="GiB">1</d:ram> - <d:buttons>soft</d:buttons> - <d:internal-storage unit="GiB">16</d:internal-storage> - <d:removable-storage unit="KiB"></d:removable-storage> - <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> - <d:gpu>PowerVR SGX540</d:gpu> - <d:abi> - armeabi - armeabi-v7a - </d:abi> - <!--dock (car, desk, tv, none)--> - <d:dock></d:dock> - <d:power-type>battery</d:power-type> - </d:hardware> - <d:software> - <d:api-level>15</d:api-level> - <d:live-wallpaper-support>true</d:live-wallpaper-support> - <d:bluetooth-profiles> - HSP - HFP - SPP - A2DP - AVRCP - OPP - PBAP - GAVDP - AVDTP - HID - HDP - PAN - </d:bluetooth-profiles> - <d:gl-version>2.0</d:gl-version> - <d:gl-extensions> - GL_EXT_discard_framebuffer - GL_EXT_multi_draw_arrays - GL_EXT_shader_texture_lod - GL_EXT_texture_format_BGRA8888 - GL_IMG_multisampled_render_to_texture - GL_IMG_program_binary - GL_IMG_read_format - GL_IMG_shader_binary - GL_IMG_texture_compression_pvrtc - GL_IMG_texture_format_BGRA8888 - GL_IMG_texture_npot - GL_OES_compressed_ETC1_RGB8_texture - GL_OES_depth_texture - GL_OES_depth24 - GL_OES_EGL_image - GL_OES_EGL_image_external - GL_OES_egl_sync - GL_OES_element_index_uint - GL_OES_fragment_precision_high - GL_OES_get_program_binary - GL_OES_mapbuffer - GL_OES_packed_depth_stencil - GL_OES_required_internalformat - GL_OES_rgb8_rgba8 - GL_OES_standard_derivatives - GL_OES_texture_float - GL_OES_texture_half_float - GL_OES_vertex_array_object - GL_OES_vertex_half_float - </d:gl-extensions> - <d:status-bar>true</d:status-bar> - </d:software> - <d:state name="Portrait" default="true"> - <d:description>The phone in portrait view</d:description> - <d:screen-orientation>port</d:screen-orientation> - <d:keyboard-state>keyssoft</d:keyboard-state> - <d:nav-state>nonav</d:nav-state> - </d:state> - <d:state name="Landscape"> - <d:description>The phone in landscape view</d:description> - <d:screen-orientation>land</d:screen-orientation> - <d:keyboard-state>keyssoft</d:keyboard-state> - <d:nav-state>nonav</d:nav-state> - </d:state> - </d:device> -</d:devices> diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_default.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_default.xml deleted file mode 100644 index 605a6c1..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_default.xml +++ /dev/null @@ -1,134 +0,0 @@ -<?xml version="1.0"?> -<d:devices - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:d="http://schemas.android.com/sdk/devices/1"> - - <d:device> - <d:name> - Galaxy Nexus - </d:name> - <d:manufacturer> - Samsung - </d:manufacturer> - <d:hardware> - <d:screen> - <d:screen-size>normal</d:screen-size> - <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> - <d:pixel-density>xhdpi</d:pixel-density> - <d:screen-ratio>long</d:screen-ratio> - <d:dimensions> - <d:x-dimension>720</d:x-dimension> - <d:y-dimension>1280</d:y-dimension> - </d:dimensions> - <d:xdpi>316</d:xdpi> - <d:ydpi>316</d:ydpi> - <d:touch> - <d:multitouch>jazz-hands</d:multitouch> - <d:mechanism>finger</d:mechanism> - <d:screen-type>capacitive</d:screen-type> - </d:touch> - </d:screen> - <d:networking> - Bluetooth - Wifi - NFC - </d:networking> - <d:sensors> - Accelerometer - Barometer - Gyroscope - Compass - GPS - ProximitySensor - </d:sensors> - <d:mic>true</d:mic> - <d:camera> - <d:location>front</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>false</d:flash> - </d:camera> - <d:camera> - <d:location>back</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>true</d:flash> - </d:camera> - <d:keyboard>nokeys</d:keyboard> - <d:nav>nonav</d:nav> - <d:ram unit="GiB">1</d:ram> - <d:buttons>soft</d:buttons> - <d:internal-storage unit="GiB">16</d:internal-storage> - <d:removable-storage unit="KiB"></d:removable-storage> - <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> - <d:gpu>PowerVR SGX540</d:gpu> - <d:abi> - armeabi - armeabi-v7a - </d:abi> - <!--dock (car, desk, tv, none)--> - <d:dock> - </d:dock> - <d:power-type>battery</d:power-type> - </d:hardware> - <d:software> - <d:api-level>14</d:api-level> - <d:live-wallpaper-support>true</d:live-wallpaper-support> - <d:bluetooth-profiles> - HSP - HFP - SPP - A2DP - AVRCP - OPP - PBAP - GAVDP - AVDTP - HID - HDP - PAN - </d:bluetooth-profiles> - <d:gl-version>2.0</d:gl-version> - <!-- - These can be gotten via - javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); - --> - <d:gl-extensions> - GL_EXT_discard_framebuffer - GL_EXT_multi_draw_arrays - GL_EXT_shader_texture_lod - GL_EXT_texture_format_BGRA8888 - GL_IMG_multisampled_render_to_texture - GL_IMG_program_binary - GL_IMG_read_format - GL_IMG_shader_binary - GL_IMG_texture_compression_pvrtc - GL_IMG_texture_format_BGRA8888 - GL_IMG_texture_npot - GL_OES_compressed_ETC1_RGB8_texture - GL_OES_depth_texture - GL_OES_depth24 - GL_OES_EGL_image - GL_OES_EGL_image_external - GL_OES_egl_sync - GL_OES_element_index_uint - GL_OES_fragment_precision_high - GL_OES_get_program_binary - GL_OES_mapbuffer - GL_OES_packed_depth_stencil - GL_OES_required_internalformat - GL_OES_rgb8_rgba8 - GL_OES_standard_derivatives - GL_OES_texture_float - GL_OES_texture_half_float - GL_OES_vertex_array_object - GL_OES_vertex_half_float - </d:gl-extensions> - <d:status-bar>true</d:status-bar> - </d:software> - <d:state name="Portrait"> - <d:description>The phone in portrait view</d:description> - <d:screen-orientation>port</d:screen-orientation> - <d:keyboard-state>keyssoft</d:keyboard-state> - <d:nav-state>nonav</d:nav-state> - </d:state> - </d:device> -</d:devices> diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_hardware.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_hardware.xml deleted file mode 100644 index fb133ad..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_hardware.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0"?> -<d:devices - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:d="http://schemas.android.com/sdk/devices/1"> - - <d:device> - <d:name> - Galaxy Nexus - </d:name> - <d:manufacturer> - Samsung - </d:manufacturer> - <d:software> - <d:api-level>14</d:api-level> - <d:live-wallpaper-support>true</d:live-wallpaper-support> - <d:bluetooth-profiles> - HSP - HFP - SPP - A2DP - AVRCP - OPP - PBAP - GAVDP - AVDTP - HID - HDP - PAN - </d:bluetooth-profiles> - <d:gl-version>2.0</d:gl-version> - <!-- - These can be gotten via - javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); - --> - <d:gl-extensions> - GL_EXT_discard_framebuffer - GL_EXT_multi_draw_arrays - GL_EXT_shader_texture_lod - GL_EXT_texture_format_BGRA8888 - GL_IMG_multisampled_render_to_texture - GL_IMG_program_binary - GL_IMG_read_format - GL_IMG_shader_binary - GL_IMG_texture_compression_pvrtc - GL_IMG_texture_format_BGRA8888 - GL_IMG_texture_npot - GL_OES_compressed_ETC1_RGB8_texture - GL_OES_depth_texture - GL_OES_depth24 - GL_OES_EGL_image - GL_OES_EGL_image_external - GL_OES_egl_sync - GL_OES_element_index_uint - GL_OES_fragment_precision_high - GL_OES_get_program_binary - GL_OES_mapbuffer - GL_OES_packed_depth_stencil - GL_OES_required_internalformat - GL_OES_rgb8_rgba8 - GL_OES_standard_derivatives - GL_OES_texture_float - GL_OES_texture_half_float - GL_OES_vertex_array_object - GL_OES_vertex_half_float - </d:gl-extensions> - <d:status-bar>true</d:status-bar> - </d:software> - <d:state name="Portrait" default="true"> - <d:description>The phone in portrait view</d:description> - <d:screen-orientation>port</d:screen-orientation> - <d:keyboard-state>keyssoft</d:keyboard-state> - <d:nav-state>nonav</d:nav-state> - </d:state> - <d:state name="Landscape"> - <d:description>The phone in landscape view</d:description> - <d:screen-orientation>land</d:screen-orientation> - <d:keyboard-state>keyssoft</d:keyboard-state> - <d:nav-state>nonav</d:nav-state> - </d:state> - </d:device> -</d:devices> diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_software.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_software.xml deleted file mode 100644 index ae709bb..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_software.xml +++ /dev/null @@ -1,80 +0,0 @@ -<?xml version="1.0"?> -<d:devices - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:d="http://schemas.android.com/sdk/devices/1"> - - <d:device> - <d:name> - Galaxy Nexus - </d:name> - <d:manufacturer> - Samsung - </d:manufacturer> - <d:hardware> - <d:screen> - <d:screen-size>normal</d:screen-size> - <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> - <d:pixel-density>xhdpi</d:pixel-density> - <d:screen-ratio>long</d:screen-ratio> - <d:dimensions> - <d:x-dimension>720</d:x-dimension> - <d:y-dimension>1280</d:y-dimension> - </d:dimensions> - <d:xdpi>316</d:xdpi> - <d:ydpi>316</d:ydpi> - <d:touch> - <d:multitouch>jazz-hands</d:multitouch> - <d:mechanism>finger</d:mechanism> - <d:screen-type>capacitive</d:screen-type> - </d:touch> - </d:screen> - <d:networking> - Bluetooth - Wifi - NFC - </d:networking> - <d:sensors> - Accelerometer - Barometer - Gyroscope - Compass - GPS - ProximitySensor - </d:sensors> - <d:mic>true</d:mic> - <d:camera> - <d:location>front</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>false</d:flash> - </d:camera> - <d:camera> - <d:location>back</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>true</d:flash> - </d:camera> - <d:keyboard>nokeys</d:keyboard> - <d:nav>nonav</d:nav> - <d:ram unit="GiB">1</d:ram> - <d:buttons>soft</d:buttons> - <d:internal-storage unit="GiB">16</d:internal-storage> - <d:removable-storage unit="KiB"></d:removable-storage> - <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> - <d:gpu>PowerVR SGX540</d:gpu> - <d:abi> - armeabi - armeabi-v7a - </d:abi> - <!--dock (car, desk, tv, none)--> - <d:dock> - </d:dock> - <!-- plugged in (never, charge, always) --> - <d:power-type>battery</d:power-type> - </d:hardware> - <d:state name="Portrait" default="true"> - <d:description>The phone in portrait view</d:description> - <d:screen-orientation>port</d:screen-orientation> - <d:keyboard-state>keyssoft</d:keyboard-state> - <d:nav-state>nonav</d:nav-state> - </d:state> - </d:device> -</d:devices> diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_states.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_states.xml deleted file mode 100644 index 8685e3b..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_no_states.xml +++ /dev/null @@ -1,129 +0,0 @@ -<?xml version="1.0"?> -<d:devices - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:d="http://schemas.android.com/sdk/devices/1"> - - <d:device> - <d:name> - Galaxy Nexus - </d:name> - <d:manufacturer> - Samsung - </d:manufacturer> - <d:hardware> - <d:screen> - <d:screen-size>normal</d:screen-size> - <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> - <d:pixel-density>xhdpi</d:pixel-density> - <d:screen-ratio>long</d:screen-ratio> - <d:dimensions> - <d:x-dimension>720</d:x-dimension> - <d:y-dimension>1280</d:y-dimension> - </d:dimensions> - <d:xdpi>316</d:xdpi> - <d:ydpi>316</d:ydpi> - <d:touch> - <d:multitouch>jazz-hands</d:multitouch> - <d:mechanism>finger</d:mechanism> - <d:screen-type>capacitive</d:screen-type> - </d:touch> - </d:screen> - <d:networking> - Bluetooth - Wifi - NFC - </d:networking> - <d:sensors> - Accelerometer - Barometer - Gyroscope - Compass - GPS - ProximitySensor - </d:sensors> - <d:mic>true</d:mic> - <d:camera> - <d:location>front</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>false</d:flash> - </d:camera> - <d:camera> - <d:location>back</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>true</d:flash> - </d:camera> - <d:keyboard>nokeys</d:keyboard> - <d:nav>nonav</d:nav> - <d:ram unit="GiB">1</d:ram> - <d:buttons>soft</d:buttons> - <d:internal-storage unit="GiB">16</d:internal-storage> - <d:removable-storage unit="KiB"></d:removable-storage> - <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> - <d:gpu>PowerVR SGX540</d:gpu> - <d:abi> - armeabi - armeabi-v7a - </d:abi> - <!--dock (car, desk, tv, none)--> - <d:dock> - </d:dock> - <!-- plugged in (never, charge, always) --> - <d:power-type>battery</d:power-type> - </d:hardware> - <d:software> - <d:api-level>14</d:api-level> - <d:live-wallpaper-support>true</d:live-wallpaper-support> - <d:bluetooth-profiles> - HSP - HFP - SPP - A2DP - AVRCP - OPP - PBAP - GAVDP - AVDTP - HID - HDP - PAN - </d:bluetooth-profiles> - <d:gl-version>2.0</d:gl-version> - <!-- - These can be gotten via - javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); - --> - <d:gl-extensions> - GL_EXT_discard_framebuffer - GL_EXT_multi_draw_arrays - GL_EXT_shader_texture_lod - GL_EXT_texture_format_BGRA8888 - GL_IMG_multisampled_render_to_texture - GL_IMG_program_binary - GL_IMG_read_format - GL_IMG_shader_binary - GL_IMG_texture_compression_pvrtc - GL_IMG_texture_format_BGRA8888 - GL_IMG_texture_npot - GL_OES_compressed_ETC1_RGB8_texture - GL_OES_depth_texture - GL_OES_depth24 - GL_OES_EGL_image - GL_OES_EGL_image_external - GL_OES_egl_sync - GL_OES_element_index_uint - GL_OES_fragment_precision_high - GL_OES_get_program_binary - GL_OES_mapbuffer - GL_OES_packed_depth_stencil - GL_OES_required_internalformat - GL_OES_rgb8_rgba8 - GL_OES_standard_derivatives - GL_OES_texture_float - GL_OES_texture_half_float - GL_OES_vertex_array_object - GL_OES_vertex_half_float - </d:gl-extensions> - <d:status-bar>true</d:status-bar> - </d:software> - </d:device> -</d:devices> diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_too_many_defaults.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_too_many_defaults.xml deleted file mode 100644 index c720a7a..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_too_many_defaults.xml +++ /dev/null @@ -1,141 +0,0 @@ -<?xml version="1.0"?> -<d:devices - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:d="http://schemas.android.com/sdk/devices/1"> - - <d:device> - <d:name> - Galaxy Nexus - </d:name> - <d:manufacturer> - Samsung - </d:manufacturer> - <d:hardware> - <d:screen> - <d:screen-size>normal</d:screen-size> - <d:diagonal-length>4.65</d:diagonal-length> <!-- In inches --> - <d:pixel-density>xhdpi</d:pixel-density> - <d:screen-ratio>long</d:screen-ratio> - <d:dimensions> - <d:x-dimension>720</d:x-dimension> - <d:y-dimension>1280</d:y-dimension> - </d:dimensions> - <d:xdpi>316</d:xdpi> - <d:ydpi>316</d:ydpi> - <d:touch> - <d:multitouch>jazz-hands</d:multitouch> - <d:mechanism>finger</d:mechanism> - <d:screen-type>capacitive</d:screen-type> - </d:touch> - </d:screen> - <d:networking> - Bluetooth - Wifi - NFC - </d:networking> - <d:sensors> - Accelerometer - Barometer - Gyroscope - Compass - GPS - ProximitySensor - </d:sensors> - <d:mic>true</d:mic> - <d:camera> - <d:location>front</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>false</d:flash> - </d:camera> - <d:camera> - <d:location>back</d:location> - <d:autofocus>true</d:autofocus> - <d:flash>true</d:flash> - </d:camera> - <d:keyboard>nokeys</d:keyboard> - <d:nav>nonav</d:nav> - <d:ram unit="GiB">1</d:ram> - <d:buttons>soft</d:buttons> - <d:internal-storage unit="GiB">16</d:internal-storage> - <d:removable-storage unit="KiB"></d:removable-storage> - <d:cpu>OMAP 4460</d:cpu> <!-- cpu type (Tegra3) freeform --> - <d:gpu>PowerVR SGX540</d:gpu> - <d:abi> - armeabi - armeabi-v7a - </d:abi> - <!--dock (car, desk, tv, none)--> - <d:dock> - </d:dock> - <!-- plugged in (never, charge, always) --> - <d:power-type>battery</d:power-type> - </d:hardware> - <d:software> - <d:api-level>14</d:api-level> - <d:live-wallpaper-support>true</d:live-wallpaper-support> - <d:bluetooth-profiles> - HSP - HFP - SPP - A2DP - AVRCP - OPP - PBAP - GAVDP - AVDTP - HID - HDP - PAN - </d:bluetooth-profiles> - <d:gl-version>2.0</d:gl-version> - <!-- - These can be gotten via - javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS); - --> - <d:gl-extensions> - GL_EXT_discard_framebuffer - GL_EXT_multi_draw_arrays - GL_EXT_shader_texture_lod - GL_EXT_texture_format_BGRA8888 - GL_IMG_multisampled_render_to_texture - GL_IMG_program_binary - GL_IMG_read_format - GL_IMG_shader_binary - GL_IMG_texture_compression_pvrtc - GL_IMG_texture_format_BGRA8888 - GL_IMG_texture_npot - GL_OES_compressed_ETC1_RGB8_texture - GL_OES_depth_texture - GL_OES_depth24 - GL_OES_EGL_image - GL_OES_EGL_image_external - GL_OES_egl_sync - GL_OES_element_index_uint - GL_OES_fragment_precision_high - GL_OES_get_program_binary - GL_OES_mapbuffer - GL_OES_packed_depth_stencil - GL_OES_required_internalformat - GL_OES_rgb8_rgba8 - GL_OES_standard_derivatives - GL_OES_texture_float - GL_OES_texture_half_float - GL_OES_vertex_array_object - GL_OES_vertex_half_float - </d:gl-extensions> - <d:status-bar>true</d:status-bar> - </d:software> - <d:state name="Portrait" default="true"> - <d:description>The phone in portrait view</d:description> - <d:screen-orientation>port</d:screen-orientation> - <d:keyboard-state>keyssoft</d:keyboard-state> - <d:nav-state>nonav</d:nav-state> - </d:state> - <d:state name="Landscape" default="true"> - <d:description>The phone in landscape view</d:description> - <d:screen-orientation>land</d:screen-orientation> - <d:keyboard-state>keyssoft</d:keyboard-state> - <d:nav-state>nonav</d:nav-state> - </d:state> - </d:device> -</d:devices> diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/frame.jpeg b/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/frame.jpeg deleted file mode 100644 index e69de29..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/frame.jpeg +++ /dev/null diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/frame.png b/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/frame.png deleted file mode 100644 index e69de29..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/frame.png +++ /dev/null diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixteen.jpeg b/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixteen.jpeg deleted file mode 100644 index e69de29..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixteen.jpeg +++ /dev/null diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixteen.png b/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixteen.png deleted file mode 100644 index e69de29..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixteen.png +++ /dev/null diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixtyfour.jpeg b/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixtyfour.jpeg deleted file mode 100644 index e69de29..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixtyfour.jpeg +++ /dev/null diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixtyfour.png b/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixtyfour.png deleted file mode 100644 index e69de29..0000000 --- a/device_validator/dvlib/src/test/resources/com/android/dvlib/extras/sixtyfour.png +++ /dev/null diff --git a/draw9patch/Android.mk b/draw9patch/Android.mk index ef2184d..ddb8707 100644 --- a/draw9patch/Android.mk +++ b/draw9patch/Android.mk @@ -20,9 +20,6 @@ LOCAL_JAVA_RESOURCE_DIRS := src LOCAL_JAR_MANIFEST := etc/manifest.txt -LOCAL_JAVA_LIBRARIES := \ - swing-worker-1.1 - LOCAL_MODULE := draw9patch LOCAL_MODULE_TAGS := debug diff --git a/draw9patch/etc/manifest.txt b/draw9patch/etc/manifest.txt index b2e3528..2616706 100644 --- a/draw9patch/etc/manifest.txt +++ b/draw9patch/etc/manifest.txt @@ -1,2 +1 @@ Main-Class: com.android.draw9patch.Application -Class-Path: swing-worker-1.1.jar diff --git a/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java b/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java index bf3754b..845ee54 100644 --- a/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java +++ b/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java @@ -685,6 +685,12 @@ class ImageEditorPanel extends JPanel { private boolean showBadPatches; private JPanel helpPanel; + private boolean drawingLine; + private int lineFromX; + private int lineFromY; + private int lineToX; + private int lineToY; + private boolean showDrawingLine; ImageViewer() { setLayout(new GridBagLayout()); @@ -735,7 +741,12 @@ class ImageEditorPanel extends JPanel { // changed state). currentButton = event.isShiftDown() ? MouseEvent.BUTTON3 : event.getButton(); currentButton = event.isControlDown() ? MouseEvent.BUTTON2 : currentButton; - paint(event.getX(), event.getY(), currentButton); + startDrawingLine(event.getX(), event.getY(), currentButton); + } + + @Override + public void mouseReleased(MouseEvent event) { + endDrawingLine(); } }); addMouseMotionListener(new MouseMotionAdapter() { @@ -743,7 +754,8 @@ class ImageEditorPanel extends JPanel { public void mouseDragged(MouseEvent event) { if (!checkLockedRegion(event.getX(), event.getY())) { // use the stored button, see note above - paint(event.getX(), event.getY(), currentButton); + + moveLine(event.getX(), event.getY()); } } @@ -754,7 +766,7 @@ class ImageEditorPanel extends JPanel { }); Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { public void eventDispatched(AWTEvent event) { - enableEraseMode((KeyEvent) event); + enableEraseMode((KeyEvent) event); } }, AWTEvent.KEY_EVENT_MASK); @@ -857,21 +869,34 @@ class ImageEditorPanel extends JPanel { } } - private void paint(int x, int y, int button) { - int color; - switch (button) { - case MouseEvent.BUTTON1: - color = BLACK_TICK; - break; - case MouseEvent.BUTTON2: - color = RED_TICK; - break; - case MouseEvent.BUTTON3: - color = 0; - break; - default: - return; + private void startDrawingLine(int x, int y, int button) { + int left = (getWidth() - size.width) / 2; + int top = helpPanel.getHeight() + (getHeight() - size.height) / 2; + + x = (x - left) / zoom; + y = (y - top) / zoom; + + int width = image.getWidth(); + int height = image.getHeight(); + if (((x == 0 || x == width - 1) && (y > 0 && y < height - 1)) + || ((x > 0 && x < width - 1) && (y == 0 || y == height - 1))) { + drawingLine = true; + lineFromX = x; + lineFromY = y; + lineToX = x; + lineToY = y; + + showDrawingLine = true; + + showCursor = false; + + repaint(); } + } + + private void moveLine(int x, int y) { + if (drawingLine == false) + return; int left = (getWidth() - size.width) / 2; int top = helpPanel.getHeight() + (getHeight() - size.height) / 2; @@ -881,16 +906,74 @@ class ImageEditorPanel extends JPanel { int width = image.getWidth(); int height = image.getHeight(); - if (((x == 0 || x == width - 1) && (y > 0 && y < height - 1)) || - ((x > 0 && x < width - 1) && (y == 0 || y == height - 1))) { - image.setRGB(x, y, color); - findPatches(); - stretchesViewer.computePatches(); - if (showBadPatches) { - findBadPatches(); + + showDrawingLine = false; + + if (((x == lineFromX) && (y > 0 && y < height - 1)) + || ((x > 0 && x < width - 1) && (y == lineFromY))) { + if (x == lineFromX || y == lineFromY) { + lineToX = x; + lineToY = y; + + showDrawingLine = true; } - repaint(); } + + repaint(); + } + + private void endDrawingLine() { + if (drawingLine == false) + return; + + drawingLine = false; + + if (showDrawingLine == false) + return; + + int color; + switch (currentButton) { + case MouseEvent.BUTTON1: + color = BLACK_TICK; + break; + case MouseEvent.BUTTON2: + color = RED_TICK; + break; + case MouseEvent.BUTTON3: + color = 0; + break; + default: + return; + } + + int x = lineFromX; + int y = lineFromY; + + int dx = 0; + int dy = 0; + + if (lineToX != lineFromX) + dx = lineToX > lineFromX ? 1 : -1; + else if (lineToY != lineFromY) + dy = lineToY > lineFromY ? 1 : -1; + + do { + image.setRGB(x, y, color); + + if (x == lineToX && y == lineToY) + break; + + x += dx; + y += dy; + } while (true); + + findPatches(); + stretchesViewer.computePatches(); + if (showBadPatches) { + findBadPatches(); + } + + repaint(); } private boolean checkLockedRegion(int x, int y) { @@ -915,8 +998,10 @@ class ImageEditorPanel extends JPanel { locked = x > 0 && x < width - 1 && y > 0 && y < height - 1; boolean previousCursor = showCursor; - showCursor = ((x == 0 || x == width - 1) && (y > 0 && y < height - 1)) || - ((x > 0 && x < width - 1) && (y == 0 || y == height - 1)); + showCursor = + !drawingLine && + ( ((x == 0 || x == width - 1) && (y > 0 && y < height - 1)) || + ((x > 0 && x < width - 1) && (y == 0 || y == height - 1)) ); if (locked != previousLock) { repaint(); @@ -989,6 +1074,32 @@ class ImageEditorPanel extends JPanel { g2.dispose(); + if (drawingLine && showDrawingLine) { + Graphics cursor = g.create(); + cursor.setXORMode(Color.WHITE); + cursor.setColor(Color.BLACK); + + x = Math.min(lineFromX, lineToX); + y = Math.min(lineFromY, lineToY); + int w = Math.abs(lineFromX - lineToX) + 1; + int h = Math.abs(lineFromY - lineToY) + 1; + + x = x * zoom; + y = y * zoom; + w = w * zoom; + h = h * zoom; + + int left = (getWidth() - size.width) / 2; + int top = helpPanel.getHeight() + (getHeight() - size.height) + / 2; + + x += left; + y += top; + + cursor.drawRect(x, y, w, h); + cursor.dispose(); + } + if (showCursor) { Graphics cursor = g.create(); cursor.setXORMode(Color.WHITE); diff --git a/draw9patch/src/com/android/draw9patch/ui/MainFrame.java b/draw9patch/src/com/android/draw9patch/ui/MainFrame.java index a272a28..ff749f4 100644 --- a/draw9patch/src/com/android/draw9patch/ui/MainFrame.java +++ b/draw9patch/src/com/android/draw9patch/ui/MainFrame.java @@ -16,24 +16,24 @@ package com.android.draw9patch.ui; +import com.android.draw9patch.graphics.GraphicsUtilities; import com.android.draw9patch.ui.action.ExitAction; import com.android.draw9patch.ui.action.OpenAction; import com.android.draw9patch.ui.action.SaveAction; -import com.android.draw9patch.graphics.GraphicsUtilities; -import javax.swing.JFrame; -import javax.swing.JMenuBar; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.ActionMap; -import javax.swing.JFileChooser; -import javax.imageio.ImageIO; import java.awt.HeadlessException; import java.awt.image.BufferedImage; import java.io.File; import java.util.concurrent.ExecutionException; -import org.jdesktop.swingworker.SwingWorker; +import javax.imageio.ImageIO; +import javax.swing.ActionMap; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.SwingWorker; public class MainFrame extends JFrame { private ActionMap actionsMap; diff --git a/draw9patch/src/com/android/draw9patch/ui/action/BackgroundAction.java b/draw9patch/src/com/android/draw9patch/ui/action/BackgroundAction.java index 85d9d4f..11bf261 100644 --- a/draw9patch/src/com/android/draw9patch/ui/action/BackgroundAction.java +++ b/draw9patch/src/com/android/draw9patch/ui/action/BackgroundAction.java @@ -16,9 +16,8 @@ package com.android.draw9patch.ui.action; -import org.jdesktop.swingworker.SwingWorker; - import javax.swing.AbstractAction; +import javax.swing.SwingWorker; public abstract class BackgroundAction extends AbstractAction { protected void executeBackgroundTask(SwingWorker<?, ?> worker) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF index 262da58..7cdb400 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF @@ -58,7 +58,6 @@ Export-Package: com.android.assetstudiolib;x-friends:="com.android.ide.eclipse.t com.android.ide.common.layout;x-friends:="com.android.ide.eclipse.tests", com.android.ide.common.layout.grid;x-friends:="com.android.ide.eclipse.tests", com.android.ide.common.layout.relative;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.common.rendering.api;x-friends:="com.android.ide.eclipse.tests", com.android.ide.common.resources.platform;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal;x-friends:="com.android.ide.eclipse.tests", @@ -112,6 +111,7 @@ Export-Package: com.android.assetstudiolib;x-friends:="com.android.ide.eclipse.t com.android.ide.eclipse.adt.internal.sdk;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.sourcelookup;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.ui;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.adt.internal.utils;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.welcome;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.wizards.actions;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.wizards.export;x-friends:="com.android.ide.eclipse.tests", @@ -119,11 +119,8 @@ Export-Package: com.android.assetstudiolib;x-friends:="com.android.ide.eclipse.t com.android.ide.eclipse.adt.internal.wizards.newxmlfile;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.wizards.templates;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.io;x-friends:="com.android.ide.eclipse.tests", - com.android.layoutlib.api;x-friends:="com.android.ide.eclipse.tests", com.android.manifmerger;x-friends:="com.android.ide.eclipse.tests", - com.android.menubar;x-friends:="com.android.ide.eclipse.tests", com.android.ninepatch;x-friends:="com.android.ide.eclipse.tests", - com.android.resources;x-friends:="com.android.ide.eclipse.tests", com.android.sdkuilib.internal.repository;x-friends:="com.android.ide.eclipse.tests", com.android.sdkuilib.internal.repository.core;x-friends:="com.android.ide.eclipse.tests", com.android.sdkuilib.internal.repository.icons;x-friends:="com.android.ide.eclipse.tests", @@ -135,7 +132,6 @@ Export-Package: com.android.assetstudiolib;x-friends:="com.android.ide.eclipse.t com.android.tools.lint.checks;x-friends:="com.android.ide.eclipse.tests", com.android.tools.lint.client.api;x-friends:="com.android.ide.eclipse.tests", com.android.tools.lint.detector.api;x-friends:="com.android.ide.eclipse.tests", - com.android.util;x-friends:="com.android.ide.eclipse.tests", freemarker.cache;x-friends:="com.android.ide.eclipse.tests", freemarker.template;x-friends:="com.android.ide.eclipse.tests", org.kxml2.io;x-friends:="com.android.ide.eclipse.tests", diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java index 752bbc6..88e9c15 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java @@ -1884,7 +1884,7 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { // --------- ILogger methods ----------- @Override - public void error(Throwable t, String format, Object... args) { + public void error(@Nullable Throwable t, @Nullable String format, Object... args) { if (t != null) { log(t, format, args); } else { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java index 223e5e5..697a0bc 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java @@ -23,6 +23,7 @@ import static org.eclipse.ui.IWorkbenchPage.MATCH_INPUT; import com.android.SdkConstants; import com.android.annotations.NonNull; import com.android.annotations.Nullable; +import com.android.ide.common.sdk.SdkVersionInfo; import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; @@ -989,7 +990,7 @@ public class AdtUtils { * @return the highest known API number */ public static int getHighestKnownApiLevel() { - return SdkConstants.HIGHEST_KNOWN_API; + return SdkVersionInfo.HIGHEST_KNOWN_API; } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java index 9c42967..df204b8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java @@ -21,7 +21,7 @@ import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler; import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler.Solution; import com.android.ide.eclipse.adt.Messages; -import com.android.sdklib.internal.repository.packages.FullRevision; +import com.android.sdklib.repository.FullRevision; import com.android.sdklib.repository.PkgProps; import org.osgi.framework.Constants; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java index 4155989..71c8263 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java @@ -28,7 +28,7 @@ import com.android.sdklib.io.FileOp; import com.android.sdklib.util.GrabProcessOutput; import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; import com.android.sdklib.util.GrabProcessOutput.Wait; -import com.android.sdkuilib.repository.ISdkChangeListener; +import com.android.sdklib.repository.ISdkChangeListener; import com.android.sdkuilib.repository.SdkUpdaterWindow; import com.android.sdkuilib.repository.SdkUpdaterWindow.SdkInvocationContext; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java index ba23c95..0f0cbcc 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java @@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.internal.build.builders; import com.android.SdkConstants; import com.android.annotations.NonNull; +import com.android.annotations.Nullable; import com.android.ide.common.xml.ManifestData; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; @@ -55,9 +56,9 @@ import com.android.sdklib.io.FileOp; import com.android.utils.ILogger; import com.android.utils.Pair; import com.android.xml.AndroidManifest; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import com.google.common.collect.Multimap; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; @@ -78,9 +79,9 @@ import org.xml.sax.SAXException; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Set; import javax.xml.parsers.ParserConfigurationException; @@ -94,6 +95,7 @@ import javax.xml.parsers.ParserConfigurationException; * </ul> * */ +@SuppressWarnings("deprecation") public class PreCompilerBuilder extends BaseBuilder { /** This ID is used in plugin.xml and in each project's .project file. @@ -806,6 +808,7 @@ public class PreCompilerBuilder extends BaseBuilder { } } + @SuppressWarnings("deprecation") private void handleBuildConfig(@SuppressWarnings("rawtypes") Map args) throws IOException, CoreException { boolean debugMode = !args.containsKey(RELEASE_REQUESTED); @@ -895,7 +898,8 @@ public class PreCompilerBuilder extends BaseBuilder { } @Override - public void error(Throwable t, String errorFormat, Object... args) { + public void error(@Nullable Throwable t, @Nullable String errorFormat, + Object... args) { errors.add(String.format(errorFormat, args)); } }), @@ -1034,6 +1038,7 @@ public class PreCompilerBuilder extends BaseBuilder { * @param proguardFile an optional path to store proguard information * @throws AbortBuildException */ + @SuppressWarnings("deprecation") private void execAapt(IProject project, IAndroidTarget projectTarget, String osOutputPath, String osResPath, String osManifestPath, IFolder packageFolder, ArrayList<IFolder> libResFolders, List<Pair<File, String>> libRFiles, @@ -1049,7 +1054,6 @@ public class PreCompilerBuilder extends BaseBuilder { // launch aapt: create the command line ArrayList<String> array = new ArrayList<String>(); - @SuppressWarnings("deprecation") String aaptPath = projectTarget.getPath(IAndroidTarget.AAPT); array.add(aaptPath); @@ -1160,71 +1164,40 @@ public class PreCompilerBuilder extends BaseBuilder { // if the project has no resources, the file could not exist. if (rFile.isFile()) { // Load the full symbols from the full R.txt file. - SymbolLoader fullSymbols = new SymbolLoader(rFile); - fullSymbols.load(); - - // simpler case of a single library - if (libRFiles.size() == 1) { - Pair<File, String> lib = libRFiles.get(0); - createRClass(fullSymbols, lib.getFirst(), lib.getSecond(), osOutputPath); - - } else { - Map<String, File> libPackages = Maps.newHashMapWithExpectedSize( - libRFiles.size()); - Set<String> duplicatePackages = Sets.newHashSet(); - - // preprocessing to figure out if there are dups in the package names of - // the libraries - for (Pair<File, String> lib : libRFiles) { - String libPackage = lib.getSecond(); - File existingPkg = libPackages.get(libPackage); - if (existingPkg != null) { - // record the dup package and keep going, in case there are all - // the same - duplicatePackages.add(libPackage); - continue; - } - - libPackages.put(libPackage, lib.getFirst()); + SymbolLoader fullSymbolValues = new SymbolLoader(rFile); + fullSymbolValues.load(); + + Multimap<String, SymbolLoader> libMap = ArrayListMultimap.create(); + + // First pass processing the libraries, collecting them by packageName, + // and ignoring the ones that have the same package name as the application + // (since that R class was already created). + + for (Pair<File, String> lib : libRFiles) { + String libPackage = lib.getSecond(); + File rText = lib.getFirst(); + + if (rText.isFile()) { + // load the lib symbols + SymbolLoader libSymbols = new SymbolLoader(rText); + libSymbols.load(); + + // store these symbols by associating them with the package name. + libMap.put(libPackage, libSymbols); } + } - // check if we have duplicate but all files are the same. - if (duplicatePackages.size() > 0) { - // possible conflict! - // detect case of all libraries == same package. - if (duplicatePackages.size() == 1 && libPackages.size() == 1 && - duplicatePackages.iterator().next().equals(libPackages.keySet().iterator().next())) { - // this is ok, all libraries have the same package. - // Make a copy of the full R class. - SymbolWriter writer = new SymbolWriter(osOutputPath, - duplicatePackages.iterator().next(), - fullSymbols, fullSymbols); - writer.write(); - } else { - StringBuilder sb = new StringBuilder(); - sb.append("The following packages have been found to be used by two or more libraries:"); - for (String pkg : duplicatePackages) { - sb.append("\n\t").append(pkg); - } - sb.append("\nNo libraries must share the same package, unless all libraries share the same packages."); - - String msg = sb.toString(); - markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); - - AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, - msg); - - throw new AbortBuildException(); - } - } else { - // no dups, all libraries have different packages. - // Conflicts with the main package have been removed already. - // Just process all the libraries. - for (Pair<File, String> lib : libRFiles) { - createRClass(fullSymbols, lib.getFirst(), lib.getSecond(), - osOutputPath); - } + // now loop on all the package names, merge all the symbols to write, + // and write them + for (String packageName : libMap.keySet()) { + Collection<SymbolLoader> symbols = libMap.get(packageName); + + SymbolWriter writer = new SymbolWriter(osOutputPath, packageName, + fullSymbolValues); + for (SymbolLoader symbolLoader : symbols) { + writer.addSymbolsToWrite(symbolLoader); } + writer.write(); } } } @@ -1283,19 +1256,6 @@ public class PreCompilerBuilder extends BaseBuilder { } } - private void createRClass(SymbolLoader fullSymbols, File libRTxtFile, String libPackage, - String osOutputPath) throws IOException { - if (libRTxtFile.isFile()) { - SymbolLoader libSymbols = new SymbolLoader(libRTxtFile); - libSymbols.load(); - - SymbolWriter writer = new SymbolWriter(osOutputPath, libPackage, libSymbols, - fullSymbols); - writer.write(); - } - } - - /** * Creates a relative {@link IPath} from a java package. * @param javaPackageName the java package. diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java index 31d4d0f..4281f19 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java @@ -377,6 +377,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener AvdInfo preferredAvd = null; if (config.mAvdName != null) { preferredAvd = avdManager.getAvd(config.mAvdName, true /*validAvdOnly*/); + } + + if (preferredAvd != null) { IAndroidTarget preferredAvdTarget = preferredAvd.getTarget(); if (preferredAvdTarget != null && !preferredAvdTarget.getVersion().canRun(minApiVersion)) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java index d7a0758..b047b1b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java @@ -25,13 +25,13 @@ import static org.eclipse.jdt.core.dom.SingleMemberAnnotation.VALUE_PROPERTY; import com.android.annotations.NonNull; import com.android.annotations.Nullable; +import com.android.ide.common.sdk.SdkVersionInfo; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtUtils; import com.android.ide.eclipse.adt.internal.editors.IconFactory; import com.android.tools.lint.checks.AnnotationDetector; import com.android.tools.lint.checks.ApiDetector; import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; import com.android.tools.lint.detector.api.Scope; import org.eclipse.core.resources.IMarker; @@ -345,7 +345,8 @@ class AddSuppressAnnotation implements IMarkerResolution2 { } Issue issue = EclipseLintClient.getRegistry().getIssue(id); - boolean isClassDetector = issue != null && issue.getScope().contains(Scope.CLASS_FILE); + boolean isClassDetector = issue != null && issue.getImplementation().getScope().contains( + Scope.CLASS_FILE); // Don't offer to suppress (with an annotation) the annotation checks if (issue == AnnotationDetector.ISSUE) { @@ -409,7 +410,7 @@ class AddSuppressAnnotation implements IMarkerResolution2 { // @TargetApi is only valid on methods and classes, not fields etc && (body instanceof MethodDeclaration || body instanceof TypeDeclaration)) { - String apiString = LintUtils.getBuildCode(api); + String apiString = SdkVersionInfo.getBuildCode(api); if (apiString == null) { apiString = Integer.toString(api); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java index 0d751cb..b77b475 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java @@ -22,13 +22,13 @@ import static com.android.SdkConstants.DOT_XML; import com.android.annotations.NonNull; import com.android.annotations.Nullable; +import com.android.ide.common.sdk.SdkVersionInfo; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtUtils; import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; import com.android.ide.eclipse.adt.internal.editors.IconFactory; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; import com.android.tools.lint.checks.ApiDetector; -import com.android.tools.lint.detector.api.LintUtils; import com.google.common.collect.Lists; import org.eclipse.core.resources.IMarker; @@ -186,7 +186,7 @@ class AddSuppressAttribute implements ICompletionProposal { if (matcher.find()) { api = Integer.parseInt(matcher.group(1)); String targetApi; - String buildCode = LintUtils.getBuildCode(api); + String buildCode = SdkVersionInfo.getBuildCode(api); if (buildCode != null) { targetApi = buildCode.toLowerCase(Locale.US); fixes.add(new AddSuppressAttribute(editor, id, marker, element, diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java index f6b18e0..45ae2c5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java @@ -105,6 +105,7 @@ import java.util.List; import java.util.Map; import java.util.WeakHashMap; +import lombok.ast.TypeReference; import lombok.ast.ecj.EcjTreeConverter; import lombok.ast.grammar.ParseProblem; import lombok.ast.grammar.Source; @@ -614,8 +615,8 @@ public class EclipseLintClient extends LintClient implements IDomParser { return ""; } - String summary = issue.getDescription(); - String explanation = issue.getExplanationAsSimpleText(); + String summary = issue.getDescription(Issue.OutputFormat.TEXT); + String explanation = issue.getExplanation(Issue.OutputFormat.TEXT); StringBuilder sb = new StringBuilder(summary.length() + explanation.length() + 20); try { @@ -1213,6 +1214,19 @@ public class EclipseLintClient extends LintClient implements IDomParser { @NonNull lombok.ast.Node compilationUnit) { } + @Override + @Nullable + public lombok.ast.Node resolve(@NonNull JavaContext context, + @NonNull lombok.ast.Node node) { + return null; + } + + @Override + @Nullable + public TypeReference getType(@NonNull JavaContext context, @NonNull lombok.ast.Node node) { + return null; + } + /* Handle for creating positions cheaply and returning full fledged locations later */ private class LocationHandle implements Handle { private File mFile; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java index feb6bb5..401703e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java @@ -37,6 +37,7 @@ import com.android.tools.lint.checks.TypographyDetector; import com.android.tools.lint.checks.UseCompoundDrawableDetector; import com.android.tools.lint.checks.UselessViewDetector; import com.android.tools.lint.detector.api.Issue; +import com.android.tools.lint.detector.api.Issue.OutputFormat; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.CoreException; @@ -109,7 +110,7 @@ abstract class LintFix implements ICompletionProposal { public String getAdditionalProposalInfo() { Issue issue = EclipseLintClient.getRegistry().getIssue(mId); if (issue != null) { - return issue.getExplanationAsHtml(); + return issue.getExplanation(OutputFormat.HTML); } return null; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java index 22fe23b..ce5fd55 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java @@ -27,6 +27,7 @@ import com.android.tools.lint.client.api.Configuration; import com.android.tools.lint.client.api.DefaultConfiguration; import com.android.tools.lint.client.api.IssueRegistry; import com.android.tools.lint.detector.api.Issue; +import com.android.tools.lint.detector.api.Issue.OutputFormat; import com.android.tools.lint.detector.api.Project; import com.android.tools.lint.detector.api.Severity; import com.android.utils.SdkUtils; @@ -488,11 +489,12 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi sb.append('\n').append('\n'); sb.append("Issue Explanation:"); sb.append('\n'); - if (issue.getExplanation() != null) { + String explanation = issue.getExplanation(Issue.OutputFormat.TEXT); + if (explanation != null && !explanation.isEmpty()) { sb.append('\n'); - sb.append(issue.getExplanationAsSimpleText()); + sb.append(explanation); } else { - sb.append(issue.getDescription()); + sb.append(issue.getDescription(Issue.OutputFormat.TEXT)); } if (issue.getMoreInfo() != null) { @@ -543,7 +545,8 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi public String getAdditionalProposalInfo() { return "Provides more information about this issue." + "<br><br>" //$NON-NLS-1$ - + EclipseLintClient.getRegistry().getIssue(mId).getExplanationAsHtml(); + + EclipseLintClient.getRegistry().getIssue(mId).getExplanation( + OutputFormat.HTML); } @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintJob.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintJob.java index 95e9f18..bd1eb72 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintJob.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintJob.java @@ -140,7 +140,7 @@ final class LintJob extends Job { if (issue == null) { continue; } - if (issue.isAdequate(scope)) { + if (issue.getImplementation().isAdequate(scope)) { marker.delete(); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java index 6e18837..02af2fd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java @@ -15,6 +15,9 @@ */ package com.android.ide.eclipse.adt.internal.preferences; +import static com.android.tools.lint.detector.api.Issue.OutputFormat.RAW; +import static com.android.tools.lint.detector.api.Issue.OutputFormat.TEXT; + import com.android.annotations.NonNull; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtUtils; @@ -448,8 +451,8 @@ public class LintPreferencePage extends PropertyPage implements IWorkbenchPrefer Object data = item != null ? item.getData() : null; if (data instanceof Issue) { Issue issue = (Issue) data; - String summary = issue.getDescription(); - String explanation = issue.getExplanation(); + String summary = issue.getDescription(Issue.OutputFormat.TEXT); + String explanation = issue.getExplanation(Issue.OutputFormat.TEXT); StringBuilder sb = new StringBuilder(summary.length() + explanation.length() + 20); sb.append(summary); @@ -568,7 +571,7 @@ public class LintPreferencePage extends PropertyPage implements IWorkbenchPrefer || issue.getCategory().getName().toLowerCase(Locale.US).startsWith(filter) || issue.getCategory().getFullName().toLowerCase(Locale.US).startsWith(filter) || issue.getId().toLowerCase(Locale.US).contains(filter) - || issue.getDescription().toLowerCase(Locale.US).contains(filter); + || issue.getDescription(RAW).toLowerCase(Locale.US).contains(filter); } private class ContentProvider extends TreeNodeContentProvider { @@ -712,7 +715,7 @@ public class LintPreferencePage extends PropertyPage implements IWorkbenchPrefer case 0: return issue.getId(); case 1: - return issue.getDescription(); + return issue.getDescription(TEXT); } return null; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/renamepackage/ApplicationPackageNameRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/renamepackage/ApplicationPackageNameRefactoring.java index f6fa6ee..406cebc 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/renamepackage/ApplicationPackageNameRefactoring.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/renamepackage/ApplicationPackageNameRefactoring.java @@ -146,7 +146,7 @@ class ApplicationPackageNameRefactoring extends Refactoring { TextEdit rewrittenImports = importVisitor.getTextEdit(); // If the import of R was potentially implicit, insert an import statement - if (cu.getPackage().getName().getFullyQualifiedName() + if (rewrittenImports != null && cu.getPackage().getName().getFullyQualifiedName() .equals(mOldPackageName.getFullyQualifiedName())) { UsageVisitor usageVisitor = new UsageVisitor(); @@ -438,10 +438,10 @@ class ApplicationPackageNameRefactoring extends Refactoring { mParser.setSource(icu); CompilationUnit cu = (CompilationUnit) mParser.createAST(null); - TextEdit text_edit = updateJavaFileImports(cu); - if (text_edit.hasChildren()) { + TextEdit textEdit = updateJavaFileImports(cu); + if (textEdit != null && textEdit.hasChildren()) { MultiTextEdit edit = new MultiTextEdit(); - edit.addChild(text_edit); + edit.addChild(textEdit); TextFileChange text_file_change = new TextFileChange(file.getName(), file); text_file_change.setTextType(SdkConstants.EXT_JAVA); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AdtConsoleSdkLog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AdtConsoleSdkLog.java index 26afebd..2396a4c 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AdtConsoleSdkLog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AdtConsoleSdkLog.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.internal.sdk; import com.android.annotations.NonNull; +import com.android.annotations.Nullable; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.utils.ILogger; @@ -28,7 +29,7 @@ public class AdtConsoleSdkLog implements ILogger { private static final String TAG = "SDK Manager"; //$NON-NLS-1$ @Override - public void error(Throwable t, String errorFormat, Object... args) { + public void error(@Nullable Throwable t, @Nullable String errorFormat, Object... args) { if (t != null) { AdtPlugin.logAndPrintError(t, TAG, "Error: " + errorFormat, args); } else { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java index 5d9c0ef..f187a3f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java @@ -235,7 +235,8 @@ public final class Sdk { final ArrayList<String> logMessages = new ArrayList<String>(); ILogger log = new ILogger() { @Override - public void error(Throwable throwable, String errorFormat, Object... arg) { + public void error(@Nullable Throwable throwable, @Nullable String errorFormat, + Object... arg) { if (errorFormat != null) { logMessages.add(String.format("Error: " + errorFormat, arg)); } diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitor.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitor.java index 65ddbd4..e99a637 100644 --- a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitor.java @@ -19,8 +19,8 @@ import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; import com.android.ddmlib.IDevice; import com.android.ddmlib.Log.LogLevel; +import com.android.ddmlib.logcat.LogCatMessage; import com.android.ddmuilib.logcat.ILogCatBufferChangeListener; -import com.android.ddmuilib.logcat.LogCatMessage; import com.android.ddmuilib.logcat.LogCatReceiver; import com.android.ddmuilib.logcat.LogCatReceiverFactory; import com.android.ide.eclipse.ddms.views.LogCatView; diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java index 3514db0..9f78c4a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java @@ -15,8 +15,8 @@ */ package com.android.ide.eclipse.ddms.views; +import com.android.ddmlib.logcat.LogCatMessage; import com.android.ddmuilib.logcat.ILogCatMessageSelectionListener; -import com.android.ddmuilib.logcat.LogCatMessage; import com.android.ddmuilib.logcat.LogCatPanel; import com.android.ddmuilib.logcat.LogCatStackTraceParser; import com.android.ide.eclipse.ddms.DdmsPlugin; diff --git a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/META-INF/MANIFEST.MF index 3554b41..d4e8b7d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/META-INF/MANIFEST.MF @@ -10,7 +10,8 @@ Bundle-ActivationPolicy: lazy Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, org.eclipse.ui.console, - com.android.ide.eclipse.ddms + com.android.ide.eclipse.ddms, + com.android.ide.eclipse.base Bundle-ClassPath: ., libs/hierarchyviewerlib.jar Export-Package: com.android.ide.eclipse.hierarchyviewer diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/.classpath b/eclipse/plugins/com.android.ide.eclipse.ndk/.classpath index d37aeb2..58a22d6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.ndk/.classpath +++ b/eclipse/plugins/com.android.ide.eclipse.ndk/.classpath @@ -5,6 +5,6 @@ <classpathentry kind="src" path="src"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/> - <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> + <classpathentry combineaccessrules="false" kind="src" path="/sdklib"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/.classpath b/eclipse/plugins/com.android.ide.eclipse.tests/.classpath index ae0cb35..ab36b62 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/.classpath +++ b/eclipse/plugins/com.android.ide.eclipse.tests/.classpath @@ -7,8 +7,6 @@ <classpathentry kind="lib" path="kxml2-2.3.0.jar"/> <classpathentry kind="lib" path="easymock.jar"/> <classpathentry kind="lib" path="sdktestutils.jar"/> - <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> - <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/> <classpathentry kind="lib" path="/plugin-adt/libs/ninepatch.jar" sourcepath="/ninepatch"/> <classpathentry kind="lib" path="/plugin-base/libs/sdklib.jar" sourcepath="/SdkLib"/> <classpathentry kind="lib" path="/plugin-adt/libs/sdkuilib.jar" sourcepath="/SdkUiLib"/> diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandlerTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandlerTest.java index 4fc2fbe..48681d0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandlerTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandlerTest.java @@ -17,7 +17,6 @@ package com.android.ide.eclipse.adt.internal.wizards.templates; import static com.android.SdkConstants.CURRENT_PLATFORM; import static com.android.SdkConstants.FD_TOOLS; -import static com.android.SdkConstants.HIGHEST_KNOWN_API; import static com.android.SdkConstants.PLATFORM_WINDOWS; import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_API; import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_BUILD_API; @@ -25,6 +24,7 @@ import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHan import com.android.annotations.NonNull; import com.android.annotations.Nullable; +import com.android.ide.common.sdk.SdkVersionInfo; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtUtils; import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient; @@ -329,14 +329,16 @@ public class TemplateHandlerTest extends SdkLoadingTestCase { } for (int minSdk = 1; - minSdk <= HIGHEST_KNOWN_API; - minSdk++) { + minSdk <= SdkVersionInfo.HIGHEST_KNOWN_API; + minSdk++) { // Don't bother checking *every* single minSdk, just pick some interesting ones if (!isInterestingApiLevel(minSdk)) { continue; } - for (int targetSdk = minSdk; targetSdk <= HIGHEST_KNOWN_API; targetSdk++) { + for (int targetSdk = minSdk; + targetSdk <= SdkVersionInfo.HIGHEST_KNOWN_API; + targetSdk++) { if (!isInterestingApiLevel(targetSdk)) { continue; } @@ -440,14 +442,16 @@ public class TemplateHandlerTest extends SdkLoadingTestCase { } for (int minSdk = 1; - minSdk <= HIGHEST_KNOWN_API; - minSdk++) { + minSdk <= SdkVersionInfo.HIGHEST_KNOWN_API; + minSdk++) { // Don't bother checking *every* single minSdk, just pick some interesting ones if (!isInterestingApiLevel(minSdk)) { continue; } - for (int targetSdk = minSdk; targetSdk <= HIGHEST_KNOWN_API; targetSdk++) { + for (int targetSdk = minSdk; + targetSdk <= SdkVersionInfo.HIGHEST_KNOWN_API; + targetSdk++) { if (!isInterestingApiLevel(targetSdk)) { continue; } diff --git a/eclipse/scripts/build.xml b/eclipse/scripts/build.xml new file mode 100644 index 0000000..67861bc --- /dev/null +++ b/eclipse/scripts/build.xml @@ -0,0 +1,133 @@ +<!-- + * 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. + +*Sample* build.xml for ADT plugin build. Do not use for actual releases. + +Note: this uses the target platform from eclipse-build-deps. +All these are newer than Eclipse 3.6.2 which is our current baseline. + +Note: for actual releases, use build_server.sh instead of this. + +To build: +$ cd sdk/eclipse/scripts +$ ant +This should create the plugins in $OUT/host/eclipse/adtplugins/build/v<timestamp>-aosp + +--> +<project name="com.android.eclipse.rcp.build" default="build"> + <!-- The timestamp for the context qualifier. --> + <tstamp> + <format property="adt.timestamp" + pattern="yyyyMMddHHmmss" /> + </tstamp> + + <!-- Root of Android Source Tree --> + <property name="ANDROID_SRC" location="../../../" /> + + <!-- Host Eclipse used for building the RCP --> + <property name="basebuilder" value="${ANDROID_SRC}/external/eclipse-basebuilder/basebuilder-3.6.2/org.eclipse.releng.basebuilder/" /> + + <!-- Source for target prebuilts --> + <property name="targetSrcDir1" value="${ANDROID_SRC}/prebuilts/eclipse/" /> + <property name="targetSrcDir2" value="${ANDROID_SRC}/prebuilts/eclipse-build-deps/" /> + + <!-- Location where build happens and resulting binaries are generated --> + <property name="outDir" value="${ANDROID_SRC}/out/host/eclipse/adtplugins/" /> + + <!-- Location where the target platform is created --> + <property name="targetDir" value="${outDir}/target" /> + + <!-- Location where the target platform is created --> + <property name="buildDir" value="${outDir}/build" /> + + <!-- Location of the sources --> + <property name="srcDir" value="${ANDROID_SRC}/sdk/eclipse/" /> + + <!-- locate launcher plugin inside eclipse --> + <path id="equinox.launcher.path"> + <fileset dir="${basebuilder}/plugins"> + <include name="org.eclipse.equinox.launcher_*.jar" /> + </fileset> + </path> + <property name="equinox.launcher" refid="equinox.launcher.path" /> + + <!-- locate pde build plugin inside eclipse --> + <path id="pde.build.dir.path"> + <dirset dir="${basebuilder}/plugins"> + <include name="org.eclipse.pde.build_*" /> + </dirset> + </path> + <property name="pde.build.dir" refid="pde.build.dir.path" /> + + <!-- create the build directory, copy plugins and features into it --> + <target name="copy_srcs"> + <mkdir dir="${buildDir}" /> + <copy todir="${buildDir}" preservelastmodified="true"> + <fileset dir="${srcDir}/"> + <include name="plugins/**" /> + <include name="features/**" /> + <exclude name="plugins/*/bin/**" /> + </fileset> + </copy> + </target> + + <!-- create target platform --> + <target name="create-target"> + <mkdir dir="${targetDir}" /> + <mkdir dir="${targetDir}/deltapack" /> + <mkdir dir="${targetDir}/repos" /> + + <unzip src="${targetSrcDir1}/deltapack/eclipse-3.7.2-delta-pack.zip" dest="${targetDir}/deltapack" overwrite="false" /> + <unzip src="${targetSrcDir1}/platform/org.eclipse.platform-3.7.2.zip" dest="${targetDir}/repos/platform" overwrite="false" /> + <unzip src="${targetSrcDir2}/cdt/cdt-master-8.0.2.zip" dest="${targetDir}/repos/cdt" overwrite="false" /> + <unzip src="${targetSrcDir2}/emf/emf-xsd-SDK-M201201231045.zip" dest="${targetDir}/repos/emf" overwrite="false" /> + <unzip src="${targetSrcDir2}/jdt/org.eclipse.jdt.source-3.7.2.zip" dest="${targetDir}/repos/jdt" overwrite="false" /> + <unzip src="${targetSrcDir2}/wtp/wtp-repo-R-3.3.2-20120210195245.zip" dest="${targetDir}/repos/wtp" overwrite="false" /> + <unzip src="${targetSrcDir2}/gef/GEF-SDK-3.7.2.zip" dest="${targetDir}/repos/gef" overwrite="false" /> + </target> + + <!-- Launch pde build --> + <target name="pde-build" depends="copy_srcs, create-target"> + <java classname="org.eclipse.equinox.launcher.Main" fork="true" failonerror="true"> + <arg value="-application" /> + <arg value="org.eclipse.ant.core.antRunner" /> + <arg value="-buildfile" /> + <arg value="${pde.build.dir}/scripts/build.xml" /> + <arg value="-data" /> + <arg value="${buildDir}/workspace" /> + <arg value="-configuration" /> + <arg value="${buildDir}/configuration" /> + <arg value="-Dbuilder=${srcDir}/buildConfig" /> + <arg value="-Dtimestamp=${timestamp}" /> + <arg value="-DeclipseLocation=${baseBuilder}" /> + <arg value="-DbuildDirectory=${buildDir}" /> + <arg value="-DbaseLocation=${targetDir}/deltapack/eclipse" /> + <arg value="-DrepoBaseLocation=${targetDir}/repos/" /> + <arg value="-DtransformedRepoLocation=${targetDir}/transformedRepos/" /> + <arg value="-DupdateSiteSource=${srcDir}/sites/external" /> + <arg value="-DforceContextQualifier=v${adt.timestamp}-aosp" /> + <classpath> + <pathelement location="${equinox.launcher}" /> + </classpath> + </java> + </target> + + <target name="clean"> + <delete dir="${outDir}" /> + <delete dir="${targetDir}" /> + </target> + + <target name="build" depends="pde-build" /> +</project> diff --git a/eclipse/scripts/create_all_symlinks.sh b/eclipse/scripts/create_all_symlinks.sh index 3130cf1..8f48fb7 100755 --- a/eclipse/scripts/create_all_symlinks.sh +++ b/eclipse/scripts/create_all_symlinks.sh @@ -211,7 +211,7 @@ LIBS="$LIBS $SDKMAN_LIBS" if [[ $PLATFORM != "windows-x86" ]]; then # liblzf doesn't build under cygwin. If necessary, this should be fixed first. - + GLD_DEST="sdk/eclipse/plugins/com.android.ide.eclipse.gldebugger/libs" GLD_LIBS="host-libprotobuf-java-2.3.0-lite liblzf" @@ -219,6 +219,22 @@ if [[ $PLATFORM != "windows-x86" ]]; then CP_FILES="$CP_FILES @:$GLD_DEST $GLD_LIBS" fi +# If some of the libs are available in prebuilts/devtools, use link to them directly +# instead of trying to rebuild them so remove them from the libs to build. Note that +# they are already listed in CP_FILES so we'll adjust the source to copy later. + +LIBS2="" +for LIB in $LIBS; do + J="prebuilts/devtools/$LIB.jar" + if [[ -f $J ]]; then + warn "## Using existing $J" + else + LIBS2="$LIBS2 $LIB" + fi +done +LIBS="$LIBS2" +unset LIBS2 + # In the mode to only echo dependencies, output them and we're done if [[ -n $ONLY_SHOW_DEPS ]]; then echo $LIBS @@ -254,7 +270,11 @@ for SRC in $CP_FILES; do fi if [[ ! -f "$SRC" ]]; then ORIG_SRC="$SRC" - SRC="out/host/$PLATFORM/framework/$SRC.jar" + # Take a prebuilts/devtools instead of a framework one if possible. + SRC="prebuilts/devtools/$SRC.jar" + if [[ ! -f "$SRC" ]]; then + SRC="out/host/$PLATFORM/framework/$ORIG_SRC.jar" + fi fi if [[ -f "$SRC" ]]; then if [[ ! -d "$DEST" ]]; then diff --git a/hierarchyviewer/etc/manifest.txt b/hierarchyviewer/etc/manifest.txt index f7ddfa9..06efd79 100644 --- a/hierarchyviewer/etc/manifest.txt +++ b/hierarchyviewer/etc/manifest.txt @@ -1,2 +1,2 @@ Main-Class: com.android.hierarchyviewer.HierarchyViewer -Class-Path: ddmlib.jar swing-worker-1.1.jar org-openide-util.jar org-netbeans-api-visual.jar +Class-Path: ddmlib.jar org-openide-util.jar org-netbeans-api-visual.jar diff --git a/hierarchyviewer/src/Android.mk b/hierarchyviewer/src/Android.mk index a578218..e84c8f8 100644 --- a/hierarchyviewer/src/Android.mk +++ b/hierarchyviewer/src/Android.mk @@ -21,7 +21,6 @@ LOCAL_JAVA_RESOURCE_DIRS := resources LOCAL_JAR_MANIFEST := ../etc/manifest.txt LOCAL_JAVA_LIBRARIES := \ ddmlib \ - swing-worker-1.1 \ org-openide-util \ org-netbeans-api-visual LOCAL_MODULE := hierarchyviewer diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/ScreenViewer.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/ScreenViewer.java index 3b0662f..bae1270 100644 --- a/hierarchyviewer/src/com/android/hierarchyviewer/ui/ScreenViewer.java +++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/ScreenViewer.java @@ -2,54 +2,53 @@ package com.android.hierarchyviewer.ui; import com.android.ddmlib.IDevice; import com.android.ddmlib.RawImage; -import com.android.hierarchyviewer.util.WorkerThread; import com.android.hierarchyviewer.scene.ViewNode; -import com.android.hierarchyviewer.ui.util.PngFileFilter; import com.android.hierarchyviewer.ui.util.IconLoader; +import com.android.hierarchyviewer.ui.util.PngFileFilter; +import com.android.hierarchyviewer.util.WorkerThread; -import javax.swing.JComponent; -import javax.swing.JScrollPane; -import javax.swing.Timer; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; -import javax.swing.BorderFactory; -import javax.swing.JLabel; -import javax.swing.JSlider; -import javax.swing.Box; -import javax.swing.JCheckBox; -import javax.swing.JButton; -import javax.swing.JFileChooser; -import javax.swing.event.ChangeListener; -import javax.swing.event.ChangeEvent; -import javax.imageio.ImageIO; - -import org.jdesktop.swingworker.SwingWorker; - -import java.io.IOException; -import java.io.File; -import java.awt.image.BufferedImage; -import java.awt.Graphics; -import java.awt.Dimension; +import java.awt.AlphaComposite; import java.awt.BorderLayout; -import java.awt.Graphics2D; import java.awt.Color; -import java.awt.Rectangle; -import java.awt.Point; -import java.awt.GridBagLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.awt.Insets; -import java.awt.FlowLayout; -import java.awt.AlphaComposite; +import java.awt.Point; +import java.awt.Rectangle; import java.awt.RenderingHints; -import java.awt.event.ActionListener; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; import java.util.concurrent.ExecutionException; +import javax.imageio.ImageIO; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import javax.swing.Timer; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + class ScreenViewer extends JPanel implements ActionListener { private final Workspace workspace; private final IDevice device; diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java index 82375e0..bfa15b3 100644 --- a/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java +++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java @@ -22,43 +22,70 @@ import com.android.hierarchyviewer.device.DeviceBridge; import com.android.hierarchyviewer.device.Window; import com.android.hierarchyviewer.laf.UnifiedContentBorder; import com.android.hierarchyviewer.scene.CaptureLoader; +import com.android.hierarchyviewer.scene.ProfilesLoader; import com.android.hierarchyviewer.scene.VersionLoader; import com.android.hierarchyviewer.scene.ViewHierarchyLoader; import com.android.hierarchyviewer.scene.ViewHierarchyScene; import com.android.hierarchyviewer.scene.ViewManager; import com.android.hierarchyviewer.scene.ViewNode; import com.android.hierarchyviewer.scene.WindowsLoader; -import com.android.hierarchyviewer.scene.ProfilesLoader; -import com.android.hierarchyviewer.ui.action.DumpDisplayListAction; -import com.android.hierarchyviewer.ui.util.PsdFileFilter; -import com.android.hierarchyviewer.util.OS; -import com.android.hierarchyviewer.util.WorkerThread; -import com.android.hierarchyviewer.ui.action.ShowDevicesAction; -import com.android.hierarchyviewer.ui.action.RequestLayoutAction; -import com.android.hierarchyviewer.ui.action.InvalidateAction; -import com.android.hierarchyviewer.ui.action.CaptureNodeAction; import com.android.hierarchyviewer.ui.action.CaptureLayersAction; -import com.android.hierarchyviewer.ui.action.RefreshWindowsAction; -import com.android.hierarchyviewer.ui.action.StopServerAction; -import com.android.hierarchyviewer.ui.action.StartServerAction; +import com.android.hierarchyviewer.ui.action.CaptureNodeAction; +import com.android.hierarchyviewer.ui.action.DumpDisplayListAction; import com.android.hierarchyviewer.ui.action.ExitAction; +import com.android.hierarchyviewer.ui.action.InvalidateAction; import com.android.hierarchyviewer.ui.action.LoadGraphAction; +import com.android.hierarchyviewer.ui.action.RefreshWindowsAction; +import com.android.hierarchyviewer.ui.action.RequestLayoutAction; import com.android.hierarchyviewer.ui.action.SaveSceneAction; -import com.android.hierarchyviewer.ui.util.PngFileFilter; -import com.android.hierarchyviewer.ui.util.IconLoader; +import com.android.hierarchyviewer.ui.action.ShowDevicesAction; +import com.android.hierarchyviewer.ui.action.StartServerAction; +import com.android.hierarchyviewer.ui.action.StopServerAction; +import com.android.hierarchyviewer.ui.model.ProfilesTableModel; import com.android.hierarchyviewer.ui.model.PropertiesTableModel; import com.android.hierarchyviewer.ui.model.ViewsTreeModel; -import com.android.hierarchyviewer.ui.model.ProfilesTableModel; -import org.jdesktop.swingworker.SwingWorker; +import com.android.hierarchyviewer.ui.util.IconLoader; +import com.android.hierarchyviewer.ui.util.PngFileFilter; +import com.android.hierarchyviewer.ui.util.PsdFileFilter; +import com.android.hierarchyviewer.util.OS; +import com.android.hierarchyviewer.util.WorkerThread; + import org.netbeans.api.visual.graph.layout.TreeGraphLayout; import org.netbeans.api.visual.model.ObjectSceneEvent; import org.netbeans.api.visual.model.ObjectSceneEventType; import org.netbeans.api.visual.model.ObjectSceneListener; import org.netbeans.api.visual.model.ObjectState; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics2D; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Image; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + import javax.imageio.ImageIO; import javax.swing.ActionMap; import javax.swing.BorderFactory; +import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -72,56 +99,31 @@ import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JProgressBar; -import javax.swing.JScrollPane; import javax.swing.JScrollBar; +import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.JSplitPane; import javax.swing.JTable; +import javax.swing.JTextField; import javax.swing.JToggleButton; import javax.swing.JToolBar; +import javax.swing.JTree; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; -import javax.swing.JTree; -import javax.swing.Box; -import javax.swing.JTextField; -import javax.swing.text.Document; -import javax.swing.text.BadLocationException; -import javax.swing.tree.TreePath; -import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.SwingWorker; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import javax.swing.event.TreeSelectionListener; import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.DocumentEvent; +import javax.swing.event.TreeSelectionListener; import javax.swing.table.DefaultTableModel; -import java.awt.image.BufferedImage; -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.GridBagLayout; -import java.awt.GridBagConstraints; -import java.awt.Insets; -import java.awt.FlowLayout; -import java.awt.Color; -import java.awt.Image; -import java.awt.Graphics2D; -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; -import java.util.concurrent.ExecutionException; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreePath; public class Workspace extends JFrame { private JLabel viewCountLabel; diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/action/BackgroundAction.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/action/BackgroundAction.java index 051e3f3..b2046fd 100644 --- a/hierarchyviewer/src/com/android/hierarchyviewer/ui/action/BackgroundAction.java +++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/action/BackgroundAction.java @@ -16,9 +16,8 @@ package com.android.hierarchyviewer.ui.action; -import org.jdesktop.swingworker.SwingWorker; - import javax.swing.AbstractAction; +import javax.swing.SwingWorker; public abstract class BackgroundAction extends AbstractAction { protected void executeBackgroundTask(SwingWorker<?, ?> worker) { diff --git a/hierarchyviewer2/app/etc/manifest.txt b/hierarchyviewer2/app/etc/manifest.txt index 0caa3c2..68222bd 100644 --- a/hierarchyviewer2/app/etc/manifest.txt +++ b/hierarchyviewer2/app/etc/manifest.txt @@ -1,2 +1,2 @@ Main-Class: com.android.hierarchyviewer.HierarchyViewerApplication -Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar sdklib.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar org.eclipse.equinox.common_3.6.0.v20100503.jar +Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar sdklib.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar org.eclipse.equinox.common_3.6.0.v20100503.jar guava-tools.jar diff --git a/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java b/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java index 38f10bc..8983f67 100644 --- a/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java +++ b/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java @@ -47,6 +47,7 @@ import com.android.hierarchyviewerlib.models.PixelPerfectModel.IImageChangeListe import com.android.hierarchyviewerlib.models.TreeViewModel; import com.android.hierarchyviewerlib.models.TreeViewModel.ITreeChangeListener; import com.android.hierarchyviewerlib.ui.DeviceSelector; +import com.android.hierarchyviewerlib.ui.InvokeMethodPrompt; import com.android.hierarchyviewerlib.ui.LayoutViewer; import com.android.hierarchyviewerlib.ui.PixelPerfect; import com.android.hierarchyviewerlib.ui.PixelPerfectControls; @@ -127,9 +128,9 @@ public class HierarchyViewerApplication extends ApplicationWindow { private LayoutViewer mLayoutViewer; private PixelPerfectLoupe mPixelPerfectLoupe; private Composite mTreeViewControls; + private InvokeMethodPrompt mInvokeMethodPrompt; private ActionButton dumpDisplayList; - private ActionButton mProfileNodes; private HierarchyViewerDirector mDirector; @@ -428,8 +429,13 @@ public class HierarchyViewerApplication extends ApplicationWindow { new TreeViewOverview(treeViewOverviewContainer); Composite propertyViewerContainer = new Composite(sideSash, SWT.BORDER); - propertyViewerContainer.setLayout(new FillLayout()); - new PropertyViewer(propertyViewerContainer); + propertyViewerContainer.setLayout(new GridLayout()); + + PropertyViewer pv = new PropertyViewer(propertyViewerContainer); + pv.setLayoutData(new GridData(GridData.FILL_BOTH)); + + mInvokeMethodPrompt = new InvokeMethodPrompt(propertyViewerContainer); + mInvokeMethodPrompt.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); Composite layoutViewerContainer = new Composite(sideSash, SWT.NONE); GridLayout layoutViewerLayout = new GridLayout(); @@ -695,6 +701,8 @@ public class HierarchyViewerApplication extends ApplicationWindow { mTreeViewButton.setSelection(true); mTreeViewButton.setImage(mTreeViewSelectedImage); + mInvokeMethodPrompt.setEnabled(hvDevice.isViewUpdateEnabled()); + mPixelPerfectButton.setSelection(false); mPixelPerfectButton.setImage(mPixelPerfectImage); diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/.classpath b/hierarchyviewer2/libs/hierarchyviewerlib/.classpath index 105d22e..779f384 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/.classpath +++ b/hierarchyviewer2/libs/hierarchyviewerlib/.classpath @@ -4,6 +4,8 @@ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/> + <classpathentry combineaccessrules="false" kind="src" path="/common"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-13.0.1.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/guava-tools/src.zip"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/> diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt b/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt index 3805b59..984261a 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt +++ b/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt @@ -1 +1 @@ -Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar org.eclipse.equinox.common_3.6.0.v20100503.jar +Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar org.eclipse.equinox.common_3.6.0.v20100503.jar guava-13.0.1.jar common.jar diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk index 1afbc92..59753e1 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk @@ -20,8 +20,10 @@ LOCAL_JAVA_RESOURCE_DIRS := ../src LOCAL_JAR_MANIFEST := ../manifest.txt -LOCAL_JAVA_LIBRARIES := ddmlib \ +LOCAL_JAVA_LIBRARIES := common \ + ddmlib \ ddmuilib \ + guava-tools \ swt \ org.eclipse.jface_3.6.2.M20110210-1200 \ org.eclipse.core.commands_3.6.0.I20100512-1500 diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java index 0f0cf65..cba35f2 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java @@ -48,6 +48,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; @@ -615,6 +616,20 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener, } } + public void invokeMethodOnSelectedView(final String method, final List<Object> args) { + final DrawableViewNode selectedNode = TreeViewModel.getModel().getSelection(); + if (selectedNode != null) { + executeInBackground("Invoke View Method", new Runnable() { + @Override + public void run() { + IHvDevice hvDevice = getHvDevice(selectedNode.viewNode.window.getDevice()); + hvDevice.invokeViewMethod(selectedNode.viewNode.window, selectedNode.viewNode, + method, args); + } + }); + } + } + public void loadAllViews() { executeInBackground("Loading all views", new Runnable() { @Override diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DdmViewDebugDevice.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DdmViewDebugDevice.java index 0a6e938..0172995 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DdmViewDebugDevice.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DdmViewDebugDevice.java @@ -144,7 +144,7 @@ public class DdmViewDebugDevice extends AbstractHvDevice implements IDeviceChang try { HandleViewDebug.listViewRoots(c, handler); } catch (IOException e) { - Log.e(TAG, e); + Log.i(TAG, "No connection to client: " + cd.getClientDescription()); continue; } @@ -215,7 +215,11 @@ public class DdmViewDebugDevice extends AbstractHvDevice implements IDeviceChang return null; } - byte[] data = handler.getData(10, TimeUnit.SECONDS); + byte[] data = handler.getData(20, TimeUnit.SECONDS); + if (data == null) { + return null; + } + String viewHierarchy = new String(data, Charset.forName("UTF-8")); return DeviceBridge.parseViewHierarchy(new BufferedReader(new StringReader(viewHierarchy)), window); @@ -370,4 +374,44 @@ public class DdmViewDebugDevice extends AbstractHvDevice implements IDeviceChang reloadWindows(); } } + + @Override + public boolean isViewUpdateEnabled() { + return true; + } + + @Override + public void invokeViewMethod(Window window, ViewNode viewNode, String method, + List<?> args) { + Client c = window.getClient(); + if (c == null) { + return; + } + + String viewRoot = window.getTitle(); + try { + HandleViewDebug.invokeMethod(c, viewRoot, viewNode.toString(), method, args.toArray()); + } catch (IOException e) { + Log.e(TAG, e); + } + } + + @Override + public boolean setLayoutParameter(Window window, ViewNode viewNode, String property, + int value) { + Client c = window.getClient(); + if (c == null) { + return false; + } + + String viewRoot = window.getTitle(); + try { + HandleViewDebug.setLayoutParameter(c, viewRoot, viewNode.toString(), property, value); + } catch (IOException e) { + Log.e(TAG, e); + return false; + } + + return true; + } } diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/HvDeviceFactory.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/HvDeviceFactory.java index c38a9cc..24a5a4f 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/HvDeviceFactory.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/HvDeviceFactory.java @@ -21,13 +21,26 @@ import com.android.ddmlib.ClientData; import com.android.ddmlib.IDevice; public class HvDeviceFactory { - private static final boolean ALWAYS_USE_VIEWSERVER = false; // for debugging + private static final String sHvProtoEnvVar = + System.getenv("ANDROID_HVPROTO"); //$NON-NLS-1$ public static IHvDevice create(IDevice device) { - if (ALWAYS_USE_VIEWSERVER) { + // default to old mechanism until the new one is fully tested + if (sHvProtoEnvVar == null || + !"ddm".equalsIgnoreCase(sHvProtoEnvVar)) { //$NON-NLS-1$ return new ViewServerDevice(device); } + // Wait for a few seconds after the device has been connected to + // allow all the clients to be initialized. Specifically, we need to wait + // until the client data is filled with the list of features supported + // by the client. + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + // ignore + } + boolean ddmViewHierarchy = false; // see if any of the clients on the device support view hierarchy via DDMS diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/IHvDevice.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/IHvDevice.java index fe8d1ba..6f1fd37 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/IHvDevice.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/IHvDevice.java @@ -24,6 +24,8 @@ import com.android.hierarchyviewerlib.ui.util.PsdFile; import org.eclipse.swt.graphics.Image; +import java.util.List; + /** Represents a device that can perform view debug operations. */ public interface IHvDevice { /** @@ -51,6 +53,10 @@ public interface IHvDevice { void requestLayout(ViewNode viewNode); void outputDisplayList(ViewNode viewNode); + boolean isViewUpdateEnabled(); + void invokeViewMethod(Window window, ViewNode viewNode, String method, List<?> args); + boolean setLayoutParameter(Window window, ViewNode viewNode, String property, int value); + void addWindowChangeListener(IWindowChangeListener l); void removeWindowChangeListener(IWindowChangeListener l); } diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewServerDevice.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewServerDevice.java index 0febcef..4445e9a 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewServerDevice.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewServerDevice.java @@ -26,6 +26,8 @@ import com.android.hierarchyviewerlib.ui.util.PsdFile; import org.eclipse.swt.graphics.Image; +import java.util.List; + public class ViewServerDevice extends AbstractHvDevice { static final String TAG = "ViewServerDevice"; @@ -146,4 +148,22 @@ public class ViewServerDevice extends AbstractHvDevice { WindowUpdater.stopListenForWindowChanges(l, mDevice); } } + + @Override + public boolean isViewUpdateEnabled() { + return false; + } + + @Override + public void invokeViewMethod(Window window, ViewNode viewNode, String method, + List<?> args) { + // not supported + } + + @Override + public boolean setLayoutParameter(Window window, ViewNode viewNode, String property, + int value) { + // not supported + return false; + } } diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/DevicePropertyEditingSupport.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/DevicePropertyEditingSupport.java new file mode 100644 index 0000000..1bbc97f --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/DevicePropertyEditingSupport.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2013 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.hierarchyviewerlib.ui; + +import com.android.SdkConstants; +import com.android.hierarchyviewerlib.device.IHvDevice; +import com.android.hierarchyviewerlib.models.ViewNode; +import com.android.hierarchyviewerlib.models.ViewNode.Property; +import com.android.utils.SdkUtils; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; + +import java.text.ParseException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public class DevicePropertyEditingSupport { + public enum PropertyType { + INTEGER, + INTEGER_OR_CONSTANT, + ENUM, + }; + + private static final List<IDevicePropertyEditor> sDevicePropertyEditors = Arrays.asList( + new LayoutPropertyEditor(), + new PaddingPropertyEditor() + ); + + public boolean canEdit(Property p) { + return getPropertyEditorFor(p) != null; + } + + private IDevicePropertyEditor getPropertyEditorFor(Property p) { + for (IDevicePropertyEditor pe: sDevicePropertyEditors) { + if (pe.canEdit(p)) { + return pe; + } + } + + return null; + } + + public PropertyType getPropertyType(Property p) { + return getPropertyEditorFor(p).getType(p); + } + + public String[] getPropertyRange(Property p) { + return getPropertyEditorFor(p).getPropertyRange(p); + } + + public boolean setValue(Collection<Property> properties, Property p, Object newValue, + ViewNode viewNode, IHvDevice device) { + return getPropertyEditorFor(p).setValue(properties, p, newValue, viewNode, device); + } + + private static String stripCategoryPrefix(String name) { + return name.substring(name.indexOf(':') + 1); + } + + private interface IDevicePropertyEditor { + boolean canEdit(Property p); + PropertyType getType(Property p); + String[] getPropertyRange(Property p); + boolean setValue(Collection<Property> properties, Property p, Object newValue, + ViewNode viewNode, IHvDevice device); + } + + private static class LayoutPropertyEditor implements IDevicePropertyEditor { + private static final Set<String> sLayoutPropertiesWithStringValues = + ImmutableSet.of(SdkConstants.ATTR_LAYOUT_WIDTH, + SdkConstants.ATTR_LAYOUT_HEIGHT, + SdkConstants.ATTR_LAYOUT_GRAVITY); + + private static final int MATCH_PARENT = -1; + private static final int FILL_PARENT = -1; + private static final int WRAP_CONTENT = -2; + + private enum LayoutGravity { + top(0x30), + bottom(0x50), + left(0x03), + right(0x05), + center_vertical(0x10), + fill_vertical(0x70), + center_horizontal(0x01), + fill_horizontal(0x07), + center(0x11), + fill(0x77), + clip_vertical(0x80), + clip_horizontal(0x08), + start(0x00800003), + end(0x00800005); + + private final int mValue; + + private LayoutGravity(int v) { + mValue = v; + } + } + + /** + * Returns true if this is a layout property with either a known string value, or an + * integer value. + */ + @Override + public boolean canEdit(Property p) { + String name = stripCategoryPrefix(p.name); + if (!name.startsWith(SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX)) { + return false; + } + + if (sLayoutPropertiesWithStringValues.contains(name)) { + return true; + } + + try { + SdkUtils.parseLocalizedInt(p.value); + return true; + } catch (ParseException e) { + return false; + } + } + + @Override + public PropertyType getType(Property p) { + String name = stripCategoryPrefix(p.name); + if (sLayoutPropertiesWithStringValues.contains(name)) { + return PropertyType.INTEGER_OR_CONSTANT; + } else { + return PropertyType.INTEGER; + } + } + + @Override + public String[] getPropertyRange(Property p) { + return new String[0]; + } + + @Override + public boolean setValue(Collection<Property> properties, Property p, Object newValue, + ViewNode viewNode, IHvDevice device) { + String name = stripCategoryPrefix(p.name); + + // nothing to do if same as current value + if (p.value.equals(newValue)) { + return false; + } + + int value = -1; + String textValue = null; + + if (SdkConstants.ATTR_LAYOUT_GRAVITY.equals(name)) { + value = 0; + StringBuilder sb = new StringBuilder(20); + for (String attr: Splitter.on('|').split((String) newValue)) { + LayoutGravity g; + try { + g = LayoutGravity.valueOf(attr); + } catch (IllegalArgumentException e) { + // ignore this gravity attribute + continue; + } + + value |= g.mValue; + + if (sb.length() > 0) { + sb.append('|'); + } + sb.append(g.name()); + } + textValue = sb.toString(); + } else if (SdkConstants.ATTR_LAYOUT_HEIGHT.equals(name) + || SdkConstants.ATTR_LAYOUT_WIDTH.equals(name)) { + // newValue is of type string, but its contents may be a named constant or a integer + String s = (String) newValue; + if (s.equalsIgnoreCase(SdkConstants.VALUE_MATCH_PARENT)) { + textValue = SdkConstants.VALUE_MATCH_PARENT; + value = MATCH_PARENT; + } else if (s.equalsIgnoreCase(SdkConstants.VALUE_FILL_PARENT)) { + textValue = SdkConstants.VALUE_FILL_PARENT; + value = FILL_PARENT; + } else if (s.equalsIgnoreCase(SdkConstants.VALUE_WRAP_CONTENT)) { + textValue = SdkConstants.VALUE_WRAP_CONTENT; + value = WRAP_CONTENT; + } + } + + if (textValue == null) { + try { + value = Integer.parseInt((String) newValue); + } catch (NumberFormatException e) { + return false; + } + } + + // attempt to set the value on the device + name = name.substring(SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX.length()); + if (device.setLayoutParameter(viewNode.window, viewNode, name, value)) { + p.value = textValue != null ? textValue : (String) newValue; + } + + return true; + } + } + + private static class PaddingPropertyEditor implements IDevicePropertyEditor { + // These names should match the field names used for padding in the Framework's View class + private static final String PADDING_LEFT = "mPaddingLeft"; //$NON-NLS-1$ + private static final String PADDING_RIGHT = "mPaddingRight"; //$NON-NLS-1$ + private static final String PADDING_TOP = "mPaddingTop"; //$NON-NLS-1$ + private static final String PADDING_BOTTOM = "mPaddingBottom"; //$NON-NLS-1$ + + private static final Set<String> sPaddingProperties = ImmutableSet.of( + PADDING_LEFT, PADDING_RIGHT, PADDING_TOP, PADDING_BOTTOM); + + @Override + public boolean canEdit(Property p) { + return sPaddingProperties.contains(stripCategoryPrefix(p.name)); + } + + @Override + public PropertyType getType(Property p) { + return PropertyType.INTEGER; + } + + @Override + public String[] getPropertyRange(Property p) { + return new String[0]; + } + + /** + * Set padding: Since the only view method is setPadding(l, t, r, b), we need access + * to all 4 padding's to update any particular one. + */ + @Override + public boolean setValue(Collection<Property> properties, Property prop, Object newValue, + ViewNode viewNode, IHvDevice device) { + int v; + try { + v = Integer.parseInt((String) newValue); + } catch (NumberFormatException e) { + return false; + } + + int pLeft = 0; + int pRight = 0; + int pTop = 0; + int pBottom = 0; + + String propName = stripCategoryPrefix(prop.name); + for (Property p: properties) { + String name = stripCategoryPrefix(p.name); + if (!sPaddingProperties.contains(name)) { + continue; + } + + if (name.equals(PADDING_LEFT)) { + pLeft = propName.equals(PADDING_LEFT) ? + v : SdkUtils.parseLocalizedInt(p.value, 0); + } else if (name.equals(PADDING_RIGHT)) { + pRight = propName.equals(PADDING_RIGHT) ? + v : SdkUtils.parseLocalizedInt(p.value, 0); + } else if (name.equals(PADDING_TOP)) { + pTop = propName.equals(PADDING_TOP) ? + v : SdkUtils.parseLocalizedInt(p.value, 0); + } else if (name.equals(PADDING_BOTTOM)) { + pBottom = propName.equals(PADDING_BOTTOM) ? + v : SdkUtils.parseLocalizedInt(p.value, 0); + } + } + + // invoke setPadding() on the device + device.invokeViewMethod(viewNode.window, viewNode, "setPadding", Arrays.asList( + Integer.valueOf(pLeft), + Integer.valueOf(pTop), + Integer.valueOf(pRight), + Integer.valueOf(pBottom) + )); + + // update the value set in the property (to avoid reading all properties back from + // the device) + prop.value = Integer.toString(v); + return true; + } + } +} diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/InvokeMethodPrompt.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/InvokeMethodPrompt.java new file mode 100644 index 0000000..944a57a --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/InvokeMethodPrompt.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2013 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.hierarchyviewerlib.ui; + +import com.android.ddmlib.Log; +import com.android.hierarchyviewerlib.HierarchyViewerDirector; +import com.android.hierarchyviewerlib.device.IHvDevice; +import com.android.hierarchyviewerlib.models.TreeViewModel; +import com.android.hierarchyviewerlib.models.TreeViewModel.ITreeChangeListener; +import com.android.hierarchyviewerlib.models.ViewNode; +import com.android.hierarchyviewerlib.ui.util.DrawableViewNode; +import com.google.common.base.CharMatcher; +import com.google.common.base.Splitter; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Text; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class InvokeMethodPrompt extends Composite implements ITreeChangeListener { + private TreeViewModel mModel; + private DrawableViewNode mSelectedNode; + private Text mText; + private static final Splitter CMD_SPLITTER = Splitter.on(CharMatcher.anyOf(", ")) + .trimResults().omitEmptyStrings(); + + public InvokeMethodPrompt(Composite parent) { + super(parent, SWT.NONE); + setLayout(new FillLayout()); + + mText = new Text(this, SWT.BORDER); + mText.addKeyListener(new KeyListener() { + @Override + public void keyReleased(KeyEvent ke) { + } + + @Override + public void keyPressed(KeyEvent ke) { + onKeyPress(ke); + } + }); + + mModel = TreeViewModel.getModel(); + mModel.addTreeChangeListener(this); + } + + private void onKeyPress(KeyEvent ke) { + if (ke.keyCode == SWT.CR) { + String cmd = mText.getText().trim(); + if (!cmd.isEmpty()) { + invokeViewMethod(cmd); + } + mText.setText(""); + } + } + + private void invokeViewMethod(String cmd) { + Iterator<String> segmentIterator = CMD_SPLITTER.split(cmd).iterator(); + + String method = null; + if (segmentIterator.hasNext()) { + method = segmentIterator.next(); + } else { + return; + } + + List<Object> args = new ArrayList<Object>(10); + while (segmentIterator.hasNext()) { + String arg = segmentIterator.next(); + + // check for boolean + if (arg.equalsIgnoreCase("true")) { + args.add(Boolean.TRUE); + continue; + } else if (arg.equalsIgnoreCase("false")) { + args.add(Boolean.FALSE); + continue; + } + + // see if last character gives a clue regarding the argument type + char typeSpecifier = Character.toUpperCase(arg.charAt(arg.length() - 1)); + try { + switch (typeSpecifier) { + case 'L': + args.add(Long.valueOf(arg.substring(0, arg.length()))); + break; + case 'D': + args.add(Double.valueOf(arg.substring(0, arg.length()))); + break; + case 'F': + args.add(Float.valueOf(arg.substring(0, arg.length()))); + break; + case 'S': + args.add(Short.valueOf(arg.substring(0, arg.length()))); + break; + case 'B': + args.add(Byte.valueOf(arg.substring(0, arg.length()))); + break; + default: // default to integer + args.add(Integer.valueOf(arg)); + break; + } + } catch (NumberFormatException e) { + Log.e("hv", "Unable to parse method argument: " + arg); + return; + } + } + + HierarchyViewerDirector.getDirector().invokeMethodOnSelectedView(method, args); + } + + @Override + public void selectionChanged() { + mSelectedNode = mModel.getSelection(); + refresh(); + } + + private boolean isViewUpdateEnabled(ViewNode viewNode) { + IHvDevice device = viewNode.window.getHvDevice(); + return device != null && device.isViewUpdateEnabled(); + } + + private void refresh() { + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + mText.setEnabled(mSelectedNode != null + && isViewUpdateEnabled(mSelectedNode.viewNode)); + } + }); + } + + @Override + public void treeChanged() { + selectionChanged(); + } + + @Override + public void viewportChanged() { + } + + @Override + public void zoomChanged() { + } +} diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java index 90d2405..9456a0a 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java @@ -16,17 +16,27 @@ package com.android.hierarchyviewerlib.ui; +import com.android.ddmuilib.ImageLoader; +import com.android.hierarchyviewerlib.HierarchyViewerDirector; +import com.android.hierarchyviewerlib.device.IHvDevice; import com.android.hierarchyviewerlib.models.TreeViewModel; -import com.android.hierarchyviewerlib.models.ViewNode; import com.android.hierarchyviewerlib.models.TreeViewModel.ITreeChangeListener; +import com.android.hierarchyviewerlib.models.ViewNode; import com.android.hierarchyviewerlib.models.ViewNode.Property; +import com.android.hierarchyviewerlib.ui.DevicePropertyEditingSupport.PropertyType; import com.android.hierarchyviewerlib.ui.util.DrawableViewNode; import com.android.hierarchyviewerlib.ui.util.TreeColumnResizer; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.ComboBoxCellEditor; +import org.eclipse.jface.viewers.EditingSupport; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.TreeViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; @@ -42,13 +52,17 @@ import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import java.util.ArrayList; +import java.util.Collection; public class PropertyViewer extends Composite implements ITreeChangeListener { private TreeViewModel mModel; private TreeViewer mTreeViewer; - private Tree mTree; + private TreeViewerColumn mValueColumn; + private PropertyValueEditingSupport mPropertyValueEditingSupport; + + private Image mImage; private DrawableViewNode mSelectedNode; @@ -144,6 +158,13 @@ public class PropertyViewer extends Composite implements ITreeChangeListener { @Override public Image getColumnImage(Object element, int column) { + if (mSelectedNode == null) { + return null; + } + if (column == 1 && mPropertyValueEditingSupport.canEdit(element)) { + return mImage; + } + return null; } @@ -188,6 +209,79 @@ public class PropertyViewer extends Composite implements ITreeChangeListener { } } + private class PropertyValueEditingSupport extends EditingSupport { + private DevicePropertyEditingSupport mDevicePropertyEditingSupport = + new DevicePropertyEditingSupport(); + + public PropertyValueEditingSupport(ColumnViewer viewer) { + super(viewer); + } + + @Override + protected boolean canEdit(Object element) { + if (mSelectedNode == null) { + return false; + } + + return element instanceof Property + && mSelectedNode.viewNode.window.getHvDevice().isViewUpdateEnabled() + && mDevicePropertyEditingSupport.canEdit((Property) element); + } + + @Override + protected CellEditor getCellEditor(Object element) { + Property p = (Property) element; + PropertyType type = mDevicePropertyEditingSupport.getPropertyType(p); + Composite parent = (Composite) getViewer().getControl(); + + switch (type) { + case INTEGER: + case INTEGER_OR_CONSTANT: + return new TextCellEditor(parent); + case ENUM: + String[] items = mDevicePropertyEditingSupport.getPropertyRange(p); + return new ComboBoxCellEditor(parent, items, SWT.READ_ONLY); + } + + return null; + } + + @Override + protected Object getValue(Object element) { + Property p = (Property) element; + PropertyType type = mDevicePropertyEditingSupport.getPropertyType(p); + + if (type == PropertyType.ENUM) { + // for enums, return the index of the current value in the list of possible values + String[] items = mDevicePropertyEditingSupport.getPropertyRange(p); + return Integer.valueOf(indexOf(p.value, items)); + } + + return ((Property) element).value; + } + + private int indexOf(String item, String[] items) { + for (int i = 0; i < items.length; i++) { + if (items[i].equals(item)) { + return i; + } + } + + return -1; + } + + @Override + protected void setValue(Object element, Object newValue) { + Property p = (Property) element; + IHvDevice device = mSelectedNode.viewNode.window.getHvDevice(); + Collection<Property> properties = mSelectedNode.viewNode.namedProperties.values(); + if (mDevicePropertyEditingSupport.setValue(properties, p, newValue, + mSelectedNode.viewNode, device)) { + doRefresh(); + } + } + } + public PropertyViewer(Composite parent) { super(parent, SWT.NONE); setLayout(new FillLayout()); @@ -202,6 +296,10 @@ public class PropertyViewer extends Composite implements ITreeChangeListener { TreeColumn valueColumn = new TreeColumn(mTree, SWT.NONE); valueColumn.setText("Value"); + mValueColumn = new TreeViewerColumn(mTreeViewer, valueColumn); + mPropertyValueEditingSupport = new PropertyValueEditingSupport(mTreeViewer); + mValueColumn.setEditingSupport(mPropertyValueEditingSupport); + mModel = TreeViewModel.getModel(); ContentProvider contentProvider = new ContentProvider(); mTreeViewer.setContentProvider(contentProvider); @@ -211,10 +309,14 @@ public class PropertyViewer extends Composite implements ITreeChangeListener { addDisposeListener(mDisposeListener); - new TreeColumnResizer(this, propertyColumn, valueColumn); + @SuppressWarnings("unused") + TreeColumnResizer resizer = new TreeColumnResizer(this, propertyColumn, valueColumn); addControlListener(mControlListener); + ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class); + mImage = imageLoader.loadImage("picker.png", Display.getDefault()); //$NON-NLS-1$ + treeChanged(); } diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/images/picker.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/picker.png Binary files differnew file mode 100644 index 0000000..8ea2bed --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/picker.png diff --git a/jobb/Android.mk b/jobb/Android.mk index 9865f30..1a58732 100644 --- a/jobb/Android.mk +++ b/jobb/Android.mk @@ -23,7 +23,7 @@ LOCAL_MODULE_TAGS := debug LOCAL_JAVA_LIBRARIES := fat32lib LOCAL_PREBUILT_JAVA_LIBRARIES := \ - ../../prebuilts/devtools/$(LOCAL_MODULE)/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) + ../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) include $(BUILD_HOST_PREBUILT) diff --git a/layoutlib_api/Android.mk b/layoutlib_api/Android.mk index 32ee53f..169cf37 100644 --- a/layoutlib_api/Android.mk +++ b/layoutlib_api/Android.mk @@ -16,7 +16,11 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java) +# The layoutlib_api code has moved to tools/base/layoutlib_api. +# The rule below uses the prebuilt layoutlib_api.jar. +# +# If you want to run the tests, cd to tools/base/layoutlib_api +# and run ./gradlew :layoutlib_api:test LOCAL_JAVA_LIBRARIES := \ common \ @@ -24,17 +28,8 @@ LOCAL_JAVA_LIBRARIES := \ LOCAL_MODULE := layoutlib_api -include $(BUILD_HOST_JAVA_LIBRARY) - -# build tests -include $(CLEAR_VARS) - -# Only compile source java files in this lib. -LOCAL_SRC_FILES := $(call all-java-files-under, src/test/java) - -LOCAL_MODULE := layoutlib_api-tests -LOCAL_MODULE_TAGS := optional +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + ../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) -LOCAL_JAVA_LIBRARIES := layoutlib_api junit +include $(BUILD_HOST_PREBUILT) -include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/AdapterBinding.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/AdapterBinding.java deleted file mode 100644 index ddcdbd5..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/AdapterBinding.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.ide.common.rendering.api; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * Describe the content of the dynamic android.widget.Adapter used to fill - * android.widget.AdapterView - */ -public class AdapterBinding implements Iterable<DataBindingItem> { - - private final int mRepeatCount; - private final List<ResourceReference> mHeaders = new ArrayList<ResourceReference>(); - private final List<DataBindingItem> mItems = new ArrayList<DataBindingItem>(); - private final List<ResourceReference> mFooters = new ArrayList<ResourceReference>(); - - public AdapterBinding(int repeatCount) { - mRepeatCount = repeatCount; - } - - public int getRepeatCount() { - return mRepeatCount; - } - - public void addHeader(ResourceReference layoutInfo) { - mHeaders.add(layoutInfo); - } - - public int getHeaderCount() { - return mHeaders.size(); - } - - public ResourceReference getHeaderAt(int index) { - return mHeaders.get(index); - } - - public void addItem(DataBindingItem item) { - mItems.add(item); - } - - public int getItemCount() { - return mItems.size(); - } - - public DataBindingItem getItemAt(int index) { - return mItems.get(index); - } - - public void addFooter(ResourceReference layoutInfo) { - mFooters.add(layoutInfo); - } - - public int getFooterCount() { - return mFooters.size(); - } - - public ResourceReference getFooterAt(int index) { - return mFooters.get(index); - } - - @Override - public Iterator<DataBindingItem> iterator() { - return mItems.iterator(); - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/AttrResourceValue.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/AttrResourceValue.java deleted file mode 100644 index 530e3d5..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/AttrResourceValue.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.ide.common.rendering.api; - -import com.android.resources.ResourceType; - -import java.util.HashMap; -import java.util.Map; - -/** - * A Resource value representing an attr resource. - * - * {@link #getValue()} will return null, instead use {@link #getAttributeValues()} to - * get the enum/flag value associated with an attribute defined in the declare-styleable. - * - */ -public class AttrResourceValue extends ResourceValue { - - private Map<String, Integer> mValueMap; - - - public AttrResourceValue(ResourceType type, String name, boolean isFramework) { - super(type, name, isFramework); - } - - /** - * Return the enum/flag integer values. - * - * @return the map of (name, integer) values. Can be null. - */ - public Map<String, Integer> getAttributeValues() { - return mValueMap; - } - - public void addValue(String name, Integer value) { - if (mValueMap == null) { - mValueMap = new HashMap<String, Integer>(); - } - - mValueMap.put(name, value); - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/Bridge.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/Bridge.java deleted file mode 100644 index a19b8d5..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/Bridge.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.api; - - -import static com.android.ide.common.rendering.api.Result.Status.NOT_IMPLEMENTED; - -import com.android.ide.common.rendering.api.Result.Status; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.util.EnumSet; -import java.util.Map; - -/** - * Entry point of the Layout Library. Extensions of this class provide a method to compute - * and render a layout. - */ -public abstract class Bridge { - - public final static int API_CURRENT = 9; - - /** - * Returns the API level of the layout library. - * <p/> - * While no methods will ever be removed, some may become deprecated, and some new ones - * will appear. - * <p/>All Layout libraries based on {@link Bridge} return at minimum an API level of 5. - */ - public abstract int getApiLevel(); - - /** - * Returns the revision of the library inside a given (layoutlib) API level. - * The true revision number of the library is {@link #getApiLevel()}.{@link #getRevision()} - */ - public int getRevision() { - return 0; - } - - /** - * Returns an {@link EnumSet} of the supported {@link Capability}. - * @return an {@link EnumSet} with the supported capabilities. - * - */ - public EnumSet<Capability> getCapabilities() { - return EnumSet.noneOf(Capability.class); - } - - /** - * Initializes the Bridge object. - * - * @param platformProperties The build properties for the platform. - * @param fontLocation the location of the fonts. - * @param enumValueMap map attrName => { map enumFlagName => Integer value }. This is typically - * read from attrs.xml in the SDK target. - * @param log a {@link LayoutLog} object. Can be null. - * @return true if success. - */ - public boolean init(Map<String, String> platformProperties, - File fontLocation, - Map<String, Map<String, Integer>> enumValueMap, - LayoutLog log) { - return false; - } - - /** - * Prepares the layoutlib to unloaded. - */ - public boolean dispose() { - return false; - } - - /** - * Starts a layout session by inflating and rendering it. The method returns a - * {@link RenderSession} on which further actions can be taken. - * - * @return a new {@link RenderSession} object that contains the result of the scene creation and - * first rendering. - */ - public RenderSession createSession(SessionParams params) { - return null; - } - - /** - * Renders a Drawable. If the rendering is successful, the result image is accessible through - * {@link Result#getData()}. It is of type {@link BufferedImage} - * @param params the rendering parameters. - * @return the result of the action. - */ - public Result renderDrawable(DrawableParams params) { - return Status.NOT_IMPLEMENTED.createResult(); - } - - /** - * Clears the resource cache for a specific project. - * <p/>This cache contains bitmaps and nine patches that are loaded from the disk and reused - * until this method is called. - * <p/>The cache is not configuration dependent and should only be cleared when a - * resource changes (at this time only bitmaps and 9 patches go into the cache). - * <p/> - * The project key provided must be similar to the one passed in {@link RenderParams}. - * - * @param projectKey the key for the project. - */ - public void clearCaches(Object projectKey) { - - } - - /** - * Utility method returning the parent of a given view object. - * - * @param viewObject the object for which to return the parent. - * - * @return a {@link Result} indicating the status of the action, and if success, the parent - * object in {@link Result#getData()} - */ - public Result getViewParent(Object viewObject) { - return NOT_IMPLEMENTED.createResult(); - } - - /** - * Utility method returning the index of a given view in its parent. - * @param viewObject the object for which to return the index. - * - * @return a {@link Result} indicating the status of the action, and if success, the index in - * the parent in {@link Result#getData()} - */ - public Result getViewIndex(Object viewObject) { - return NOT_IMPLEMENTED.createResult(); - } - - /** - * Utility method returning the baseline value for a given view object. This basically returns - * View.getBaseline(). - * - * @param viewObject the object for which to return the index. - * - * @return the baseline value or -1 if not applicable to the view object or if this layout - * library does not implement this method. - * - * @deprecated use the extended ViewInfo. - */ - @Deprecated - public Result getViewBaseline(Object viewObject) { - return NOT_IMPLEMENTED.createResult(); - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/Capability.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/Capability.java deleted file mode 100644 index 5ad438d..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/Capability.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.api; - -/** - * Enum describing the layout bridge capabilities. - * - */ -public enum Capability { - /** Ability to render at full size, as required by the layout, and unbound by the screen */ - UNBOUND_RENDERING, - /** Ability to override the background of the rendering with transparency using - * {@link SessionParams#setOverrideBgColor(int)} */ - CUSTOM_BACKGROUND_COLOR, - /** Ability to call {@link RenderSession#render()} and {@link RenderSession#render(long)}. */ - RENDER, - /** Ability to ask for a layout only with no rendering through - * {@link SessionParams#setLayoutOnly()} - */ - LAYOUT_ONLY, - /** - * Ability to control embedded layout parsers through {@link ILayoutPullParser#getParser(String)} - */ - EMBEDDED_LAYOUT, - /** Ability to call<br> - * {@link RenderSession#insertChild(Object, ILayoutPullParser, int, IAnimationListener)}<br> - * {@link RenderSession#moveChild(Object, Object, int, java.util.Map, IAnimationListener)}<br> - * {@link RenderSession#setProperty(Object, String, String)}<br> - * The method that receives an animation listener can only use it if the - * ANIMATED_VIEW_MANIPULATION, or FULL_ANIMATED_VIEW_MANIPULATION is also supported. - */ - VIEW_MANIPULATION, - /** Ability to play animations with<br> - * {@link RenderSession#animate(Object, String, boolean, IAnimationListener)} - */ - PLAY_ANIMATION, - /** - * Ability to manipulate views with animation, as long as the view does not change parent. - * {@link RenderSession#insertChild(Object, ILayoutPullParser, int, IAnimationListener)}<br> - * {@link RenderSession#moveChild(Object, Object, int, java.util.Map, IAnimationListener)}<br> - * {@link RenderSession#removeChild(Object, IAnimationListener)}<br> - */ - ANIMATED_VIEW_MANIPULATION, - /** - * Ability to move views (even into a different ViewGroup) with animation. - * see {@link RenderSession#moveChild(Object, Object, int, java.util.Map, IAnimationListener)} - */ - FULL_ANIMATED_VIEW_MANIPULATION, - ADAPTER_BINDING, - EXTENDED_VIEWINFO, - /** - * Ability to properly resize nine-patch assets. - */ - FIXED_SCALABLE_NINE_PATCH; -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DataBindingItem.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DataBindingItem.java deleted file mode 100644 index 2a93f15..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DataBindingItem.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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.ide.common.rendering.api; - -import com.android.resources.ResourceType; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - * A data binding item. It contain a {@link ResourceReference} to the view used to represent it. - * It also contains how many items of this type the AdapterView should display. - * - * It can also contain an optional list of children in case the AdapterView is an - * ExpandableListView. In this case, the count value is used as a repeat count for the children, - * similar to {@link AdapterBinding#getRepeatCount()}. - * - */ -public class DataBindingItem implements Iterable<DataBindingItem> { - private final ResourceReference mReference; - private final int mCount; - private List<DataBindingItem> mChildren; - - public DataBindingItem(ResourceReference reference, int count) { - mReference = reference; - mCount = count; - } - - public DataBindingItem(String name, boolean platformLayout, int count) { - this(new ResourceReference(name, platformLayout), count); - } - - public DataBindingItem(String name, boolean platformLayout) { - this(name, platformLayout, 1); - } - - public DataBindingItem(String name, int count) { - this(name, false /*platformLayout*/, count); - } - - public DataBindingItem(String name) { - this(name, false /*platformLayout*/, 1); - } - - /** - * Returns the {@link ResourceReference} for the view. The {@link ResourceType} for the - * referenced resource is implied to be {@link ResourceType#LAYOUT}. - */ - public ResourceReference getViewReference() { - return mReference; - } - - /** - * The repeat count for this object or the repeat count for the children if there are any. - */ - public int getCount() { - return mCount; - } - - public void addChild(DataBindingItem child) { - if (mChildren == null) { - mChildren = new ArrayList<DataBindingItem>(); - } - - mChildren.add(child); - } - - public List<DataBindingItem> getChildren() { - if (mChildren != null) { - return mChildren; - } - - return Collections.emptyList(); - } - - @Override - public Iterator<DataBindingItem> iterator() { - List<DataBindingItem> list = getChildren(); - return list.iterator(); - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DeclareStyleableResourceValue.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DeclareStyleableResourceValue.java deleted file mode 100644 index a8f269f..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DeclareStyleableResourceValue.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.ide.common.rendering.api; - -import com.android.resources.ResourceType; - -import java.util.HashMap; -import java.util.Map; - -/** - * A Resource value representing a declare-styleable resource. - * - * {@link #getValue()} will return null, instead use {@link #getAttributeValues(String)} to - * get the enum/flag value associated with an attribute defined in the declare-styleable. - * - * @deprecated This class is broken as it does not handle the namespace for each attribute. - * Thankfully, newer versions of layoutlib don't actually use it, so we just keep it as is for - * backward compatibility on older layoutlibs. - * - */ -@Deprecated -public class DeclareStyleableResourceValue extends ResourceValue { - - private Map<String, AttrResourceValue> mAttrMap; - - public DeclareStyleableResourceValue(ResourceType type, String name, boolean isFramework) { - super(type, name, isFramework); - } - - /** - * Return the enum/flag integer value for a given attribute. - * @param name the name of the attribute - * @return the map of (name, integer) values. - */ - public Map<String, Integer> getAttributeValues(String name) { - if (mAttrMap != null) { - AttrResourceValue attr = mAttrMap.get(name); - if (attr != null) { - return attr.getAttributeValues(); - } - } - - return null; - } - - public Map<String, AttrResourceValue> getAllAttributes() { - return mAttrMap; - } - - public void addValue(AttrResourceValue attr) { - if (mAttrMap == null) { - mAttrMap = new HashMap<String, AttrResourceValue>(); - } - - mAttrMap.put(attr.getName(), attr); - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DensityBasedResourceValue.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DensityBasedResourceValue.java deleted file mode 100644 index 5add715..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DensityBasedResourceValue.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.rendering.api; - -import com.android.layoutlib.api.IDensityBasedResourceValue; -import com.android.resources.ResourceType; - -@SuppressWarnings("deprecation") -public class DensityBasedResourceValue extends ResourceValue implements IDensityBasedResourceValue { - - private com.android.resources.Density mDensity; - - public DensityBasedResourceValue(ResourceType type, String name, String value, - com.android.resources.Density density, boolean isFramework) { - super(type, name, value, isFramework); - mDensity = density; - } - - /** - * Returns the density for which this resource is configured. - * @return the density. - */ - public com.android.resources.Density getResourceDensity() { - return mDensity; - } - - /** Legacy method, do not call - * @deprecated use {@link #getResourceDensity()} instead. - */ - @Override - @Deprecated - public Density getDensity() { - return Density.getEnum(mDensity.getDpiValue()); - } - - @Override - public String toString() { - return "DensityBasedResourceValue [" - + getResourceType() + "/" + getName() + " = " + getValue() - + " (density:" + mDensity +", framework:" + isFramework() + ")]"; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((mDensity == null) ? 0 : mDensity.hashCode()); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - DensityBasedResourceValue other = (DensityBasedResourceValue) obj; - if (mDensity == null) { - if (other.mDensity != null) - return false; - } else if (!mDensity.equals(other.mDensity)) - return false; - return true; - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DrawableParams.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DrawableParams.java deleted file mode 100644 index b566ad3..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/DrawableParams.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.ide.common.rendering.api; - - -/** - * Rendering parameters for {@link Bridge#renderDrawable(DrawableParams)} - * - */ -public class DrawableParams extends RenderParams { - - private final ResourceValue mDrawable; - - /** - * Builds a param object with all the necessary parameters to render a drawable with - * {@link Bridge#renderDrawable(DrawableParams)} - * - * @param drawable the {@link ResourceValue} identifying the drawable. - * @param projectKey An Object identifying the project. This is used for the cache mechanism. - * @param hardwareConfig the {@link HardwareConfig}. - * @param renderResources a {@link RenderResources} object providing access to the resources. - * @param projectCallback The {@link IProjectCallback} object to get information from - * the project. - * @param minSdkVersion the minSdkVersion of the project - * @param targetSdkVersion the targetSdkVersion of the project - * @param log the object responsible for displaying warning/errors to the user. - */ - public DrawableParams( - ResourceValue drawable, - Object projectKey, - HardwareConfig hardwareConfig, - RenderResources renderResources, - IProjectCallback projectCallback, - int minSdkVersion, int targetSdkVersion, - LayoutLog log) { - super(projectKey, hardwareConfig, - renderResources, projectCallback, minSdkVersion, targetSdkVersion, log); - mDrawable = drawable; - } - - public DrawableParams(DrawableParams params) { - super(params); - mDrawable = params.mDrawable; - } - - public ResourceValue getDrawable() { - return mDrawable; - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java deleted file mode 100644 index 89f1424..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.ide.common.rendering.api; - -import com.android.resources.Density; -import com.android.resources.ScreenOrientation; -import com.android.resources.ScreenSize; - -/** - * Hardware configuration for the rendering. - * This is immutable. - * - * @since 9 - */ -public class HardwareConfig { - - private final int mScreenWidth; - private final int mScreenHeight; - private final Density mDensity; - private final float mXdpi; - private final float mYdpi; - private final ScreenOrientation mOrientation; - private final ScreenSize mScreenSize; - - private final boolean mSoftwareButtons; - - public HardwareConfig( - int screenWidth, - int screenHeight, - Density density, - float xdpi, - float ydpi, - ScreenSize screenSize, - ScreenOrientation orientation, - boolean softwareButtons) { - mScreenWidth = screenWidth; - mScreenHeight = screenHeight; - mDensity = density; - mXdpi = xdpi; - mYdpi = ydpi; - mScreenSize = screenSize; - mOrientation = orientation; - mSoftwareButtons = softwareButtons; - } - - public int getScreenWidth() { - return mScreenWidth; - } - - public int getScreenHeight() { - return mScreenHeight; - } - - public Density getDensity() { - return mDensity; - } - - public float getXdpi() { - return mXdpi; - } - - public float getYdpi() { - return mYdpi; - } - - public ScreenSize getScreenSize() { - return mScreenSize; - } - - public ScreenOrientation getOrientation() { - return mOrientation; - } - - public boolean hasSoftwareButtons() { - return mSoftwareButtons; - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/IAnimationListener.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/IAnimationListener.java deleted file mode 100644 index 81a2320..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/IAnimationListener.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.api; - - -import java.awt.image.BufferedImage; - -public interface IAnimationListener { - /** - * Called when a new animation frame is available for display. - * - * <p>The {@link RenderSession} object is provided as a convenience. It should be queried - * for the image through {@link RenderSession#getImage()}. - * - * <p>If no {@link IImageFactory} is used, then each new animation frame will be rendered - * in its own new {@link BufferedImage} object. However if an image factory is used, and it - * always re-use the same object, then the image is only guaranteed to be valid during - * this method call. As soon as this method return the image content will be overridden - * with new drawing. - * - */ - void onNewFrame(RenderSession scene); - - /** - * Called when the animation is done playing. - */ - void done(Result result); - - /** - * Return true to cancel the animation. - */ - boolean isCanceled(); - -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/IImageFactory.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/IImageFactory.java deleted file mode 100644 index 7681243..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/IImageFactory.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.api; - - -import java.awt.image.BufferedImage; - -/** - * Image Factory Interface. - * - * An Image factory's task is to create the {@link BufferedImage} into which the scene will be - * rendered. The goal is to let the layoutlib caller create an image that's optimized for its use - * case. - * - * If no factory is passed in {@link RenderParams#setImageFactory(IImageFactory)}, then a default - * {@link BufferedImage} of type {@link BufferedImage#TYPE_INT_ARGB} is created. - * - */ -public interface IImageFactory { - - /** - * Creates a buffered image with the given size - * @param width the width of the image - * @param height the height of the image - * @return a new (or reused) BufferedImage of the given size. - */ - BufferedImage getImage(int width, int height); -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ILayoutPullParser.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ILayoutPullParser.java deleted file mode 100644 index 9c0e97b..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ILayoutPullParser.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.rendering.api; - -import org.xmlpull.v1.XmlPullParser; - -/** - * Extended version of {@link XmlPullParser} to use with - * {@link Bridge#createSession(SessionParams)} - */ -public interface ILayoutPullParser extends XmlPullParser { - - /** - * Returns a cookie for the current XML node. - * <p/>This cookie will be passed back in the {@link ViewInfo} objects, allowing association - * of a particular XML node with its result from the layout computation. - * - * @see ViewInfo#getCookie() - */ - Object getViewCookie(); - - /** - * Returns a custom parser for the layout of the given name. - * @param layoutName the name of the layout. - * @return returns a custom parser or null if no custom parsers are needed. - * - * @deprecated use {@link IProjectCallback#getParser(String)} instead - */ - @Deprecated - ILayoutPullParser getParser(String layoutName); -} - diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java deleted file mode 100644 index a88b0d3..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.rendering.api; - -import com.android.resources.ResourceType; -import com.android.util.Pair; - -import java.net.URL; - -/** - * Callback for project information needed by the Layout Library. - * Classes implementing this interface provide methods giving access to some project data, like - * resource resolution, namespace information, and instantiation of custom view. - */ -public interface IProjectCallback { - - public enum ViewAttribute { - TEXT(String.class), - IS_CHECKED(Boolean.class), - SRC(URL.class), - COLOR(Integer.class); - - private final Class<?> mClass; - - private ViewAttribute(Class<?> theClass) { - mClass = theClass; - } - - public Class<?> getAttributeClass() { - return mClass; - } - } - - /** - * Loads a custom view with the given constructor signature and arguments. - * @param name The fully qualified name of the class. - * @param constructorSignature The signature of the class to use - * @param constructorArgs The arguments to use on the constructor - * @return A newly instantiated android.view.View object. - * @throws ClassNotFoundException - * @throws Exception - */ - @SuppressWarnings("unchecked") - Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs) - throws ClassNotFoundException, Exception; - - /** - * Returns the namespace of the application. - * <p/>This lets the Layout Lib load custom attributes for custom views. - */ - String getNamespace(); - - /** - * Resolves the id of a resource Id. - * <p/>The resource id is the value of a <code>R.<type>.<name></code>, and - * this method will return both the type and name of the resource. - * @param id the Id to resolve. - * @return a Pair of {@link ResourceType} and resource name, or null if the id - * does not match any resource. - */ - @SuppressWarnings("deprecation") - Pair<ResourceType, String> resolveResourceId(int id); - - /** - * Resolves the id of a resource Id of type int[] - * <p/>The resource id is the value of a R.styleable.<name>, and this method will - * return the name of the resource. - * @param id the Id to resolve. - * @return the name of the resource or <code>null</code> if not found. - */ - String resolveResourceId(int[] id); - - /** - * Returns the id of a resource. - * <p/>The provided type and name must match an existing constant defined as - * <code>R.<type>.<name></code>. - * @param type the type of the resource - * @param name the name of the resource - * @return an Integer containing the resource Id, or <code>null</code> if not found. - */ - Integer getResourceId(ResourceType type, String name); - - /** - * Returns a custom parser for the layout of the given name. - * @param layoutName the name of the layout. - * @return returns a custom parser or null if no custom parsers are needed. - * @deprecated This is replaced by {@link #getParser(ResourceValue)} but older version - * of the layoutlib (before API7) will still call this method. - */ - @Deprecated - ILayoutPullParser getParser(String layoutName); - - /** - * Returns a custom parser for a given layout. - * @param layoutResource The layout. - * @return returns a custom parser or null if no custom parsers are needed. - */ - ILayoutPullParser getParser(ResourceValue layoutResource); - - /** - * Returns the value of an item used by an adapter. - * @param adapterView The {@link ResourceReference} for the adapter view info. - * @param adapterCookie the view cookie for this particular view. - * @param itemRef the {@link ResourceReference} for the layout used by the adapter item. - * @param fullPosition the position of the item in the full list. - * @param positionPerType the position of the item if only items of the same type are - * considered. If there is only one type of items, this is the same as - * <var>fullPosition</var>. - * @param fullParentPosition the full position of the item's parent. This is only - * valid if the adapter view is an ExpandableListView. - * @param parentPositionPerType the position of the parent's item, only considering items - * of the same type. This is only valid if the adapter view is an ExpandableListView. - * If there is only one type of items, this is the same as <var>fullParentPosition</var>. - * @param viewRef The {@link ResourceReference} for the view we're trying to fill. - * @param viewAttribute the attribute being queried. - * @param defaultValue the default value for this attribute. The object class matches the - * class associated with the {@link ViewAttribute}. - * @return the item value or null if there's no value. - * - * @see ViewAttribute#getAttributeClass() - */ - Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie, - ResourceReference itemRef, - int fullPosition, int positionPerType, - int fullParentPosition, int parentPositionPerType, - ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue); - - /** - * Returns an adapter binding for a given adapter view. - * This is only called if {@link SessionParams} does not have an {@link AdapterBinding} for - * the given {@link ResourceReference} already. - * - * @param adapterViewRef the reference of adapter view to return the adapter binding for. - * @param adapterCookie the view cookie for this particular view. - * @param viewObject the view object for the adapter. - * @return an adapter binding for the given view or null if there's no data. - */ - AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie, - Object viewObject); -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java deleted file mode 100644 index df29537..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.api; - -/** - * Log class for actions executed through {@link Bridge} and {@link RenderSession}. - */ -public class LayoutLog { - /** - * Prefix for resource warnings/errors. This is not meant to be used as-is by the Layout - * Library, but is there to help test against a wider type of warning/error. - * <p/> - * {@code tag.startsWith(LayoutLog.TAG_RESOURCE_PREFIX} will test if the tag is any type - * of resource warning/error - */ - public final static String TAG_RESOURCES_PREFIX = "resources."; - - /** - * Prefix for matrix warnings/errors. This is not meant to be used as-is by the Layout - * Library, but is there to help test against a wider type of warning/error. - * <p/> - * {@code tag.startsWith(LayoutLog.TAG_MATRIX_PREFIX} will test if the tag is any type - * of matrix warning/error - */ - public final static String TAG_MATRIX_PREFIX = "matrix."; - - /** - * Tag for unsupported feature that can have a big impact on the rendering. For instance, aild - * access. - */ - public final static String TAG_UNSUPPORTED = "unsupported"; - - /** - * Tag for error when something really unexpected happens. - */ - public final static String TAG_BROKEN = "broken"; - - /** - * Tag for resource resolution failure. - * In this case the warning/error data object will be a ResourceValue containing the type - * and name of the resource that failed to resolve - */ - public final static String TAG_RESOURCES_RESOLVE = TAG_RESOURCES_PREFIX + "resolve"; - - /** - * Tag for resource resolution failure, specifically for theme attributes. - * In this case the warning/error data object will be a ResourceValue containing the type - * and name of the resource that failed to resolve - */ - public final static String TAG_RESOURCES_RESOLVE_THEME_ATTR = TAG_RESOURCES_RESOLVE + ".theme"; - - /** - * Tag for failure when reading the content of a resource file. - */ - public final static String TAG_RESOURCES_READ = TAG_RESOURCES_PREFIX + "read"; - - /** - * Tag for wrong format in a resource value. - */ - public final static String TAG_RESOURCES_FORMAT = TAG_RESOURCES_PREFIX + "format"; - - /** - * Fidelity Tag used when a non affine transformation matrix is used in a Java API. - */ - public final static String TAG_MATRIX_AFFINE = TAG_MATRIX_PREFIX + "affine"; - - /** - * Tag used when a matrix cannot be inverted. - */ - public final static String TAG_MATRIX_INVERSE = TAG_MATRIX_PREFIX + "inverse"; - - /** - * Fidelity Tag used when a mask filter type is used but is not supported. - */ - public final static String TAG_MASKFILTER = "maskfilter"; - - /** - * Fidelity Tag used when a draw filter type is used but is not supported. - */ - public final static String TAG_DRAWFILTER = "drawfilter"; - - /** - * Fidelity Tag used when a path effect type is used but is not supported. - */ - public final static String TAG_PATHEFFECT = "patheffect"; - - /** - * Fidelity Tag used when a color filter type is used but is not supported. - */ - public final static String TAG_COLORFILTER = "colorfilter"; - - /** - * Fidelity Tag used when a rasterize type is used but is not supported. - */ - public final static String TAG_RASTERIZER = "rasterizer"; - - /** - * Fidelity Tag used when a shader type is used but is not supported. - */ - public final static String TAG_SHADER = "shader"; - - /** - * Fidelity Tag used when a xfermode type is used but is not supported. - */ - public final static String TAG_XFERMODE = "xfermode"; - - /** - * Logs a warning. - * @param tag a tag describing the type of the warning - * @param message the message of the warning - * @param data an optional data bundle that the client can use to improve the warning display. - */ - public void warning(String tag, String message, Object data) { - } - - /** - * Logs a fidelity warning. - * - * This type of warning indicates that the render will not be - * the same as the rendering on a device due to limitation of the Java rendering API. - * - * @param tag a tag describing the type of the warning - * @param message the message of the warning - * @param throwable an optional Throwable that triggered the warning - * @param data an optional data bundle that the client can use to improve the warning display. - */ - public void fidelityWarning(String tag, String message, Throwable throwable, Object data) { - } - - /** - * Logs an error. - * - * @param tag a tag describing the type of the error - * @param message the message of the error - * @param data an optional data bundle that the client can use to improve the error display. - */ - public void error(String tag, String message, Object data) { - } - - /** - * Logs an error, and the {@link Throwable} that triggered it. - * - * @param tag a tag describing the type of the error - * @param message the message of the error - * @param throwable the Throwable that triggered the error - * @param data an optional data bundle that the client can use to improve the error display. - */ - public void error(String tag, String message, Throwable throwable, Object data) { - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/MergeCookie.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/MergeCookie.java deleted file mode 100644 index ce5d21d..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/MergeCookie.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.ide.common.rendering.api; - -/** - * Special wrapper class used in special case for {@link ILayoutPullParser#getViewCookie()}. - * <p/> - * When an {@code include} tag points to a layout with a {@code merge} top level item, there is no - * top level item that can use the {@code include} item as cookie. - * <p/> - * This class is used as a cookie for all items under the {@code merge} (while referencing the - * original {@code include} cookie) to make it easy on the client to group all merged items - * into a single outline item. - * - */ -public final class MergeCookie { - - private final Object mCookie; - - public MergeCookie(Object cookie) { - mCookie = cookie; - - } - - public Object getCookie() { - return mCookie; - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/RenderParams.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/RenderParams.java deleted file mode 100644 index 2e53f14..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/RenderParams.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.api; - -import com.android.resources.Density; -import com.android.resources.ScreenSize; - -/** - * Base class for rendering parameters. This include the generic parameters but not what needs - * to be rendered or additional parameters. - * - */ -public abstract class RenderParams { - - public final static long DEFAULT_TIMEOUT = 250; //ms - - private final Object mProjectKey; - private final HardwareConfig mHardwareConfig; - private final RenderResources mRenderResources; - private final IProjectCallback mProjectCallback; - private final int mMinSdkVersion; - private final int mTargetSdkVersion; - private final LayoutLog mLog; - - private boolean mCustomBackgroundEnabled; - private int mCustomBackgroundColor; - private long mTimeout; - - private IImageFactory mImageFactory = null; - - private String mAppIcon = null; - private String mAppLabel = null; - private String mLocale = null; - private boolean mForceNoDecor; - - /** - * - * @param projectKey An Object identifying the project. This is used for the cache mechanism. - * @param hardwareConfig the {@link HardwareConfig}. - * @param renderResources a {@link RenderResources} object providing access to the resources. - * @param projectCallback The {@link IProjectCallback} object to get information from - * the project. - * @param minSdkVersion the minSdkVersion of the project - * @param targetSdkVersion the targetSdkVersion of the project - * @param log the object responsible for displaying warning/errors to the user. - */ - public RenderParams( - Object projectKey, - HardwareConfig hardwareConfig, - RenderResources renderResources, - IProjectCallback projectCallback, - int minSdkVersion, int targetSdkVersion, - LayoutLog log) { - mProjectKey = projectKey; - mHardwareConfig = hardwareConfig; - mRenderResources = renderResources; - mProjectCallback = projectCallback; - mMinSdkVersion = minSdkVersion; - mTargetSdkVersion = targetSdkVersion; - mLog = log; - mCustomBackgroundEnabled = false; - mTimeout = DEFAULT_TIMEOUT; - } - - /** - * Copy constructor. - */ - public RenderParams(RenderParams params) { - mProjectKey = params.mProjectKey; - mHardwareConfig = params.mHardwareConfig; - mRenderResources = params.mRenderResources; - mProjectCallback = params.mProjectCallback; - mMinSdkVersion = params.mMinSdkVersion; - mTargetSdkVersion = params.mTargetSdkVersion; - mLog = params.mLog; - mCustomBackgroundEnabled = params.mCustomBackgroundEnabled; - mCustomBackgroundColor = params.mCustomBackgroundColor; - mTimeout = params.mTimeout; - mImageFactory = params.mImageFactory; - mAppIcon = params.mAppIcon; - mAppLabel = params.mAppLabel; - mLocale = params.mLocale; - mForceNoDecor = params.mForceNoDecor; - } - - public void setOverrideBgColor(int color) { - mCustomBackgroundEnabled = true; - mCustomBackgroundColor = color; - } - - public void setTimeout(long timeout) { - mTimeout = timeout; - } - - public void setImageFactory(IImageFactory imageFactory) { - mImageFactory = imageFactory; - } - - public void setAppIcon(String appIcon) { - mAppIcon = appIcon; - } - - public void setAppLabel(String appLabel) { - mAppLabel = appLabel; - } - - public void setLocale(String locale) { - mLocale = locale; - } - - public void setForceNoDecor() { - mForceNoDecor = true; - } - - public Object getProjectKey() { - return mProjectKey; - } - - public HardwareConfig getHardwareConfig() { - return mHardwareConfig; - } - - public int getMinSdkVersion() { - return mMinSdkVersion; - } - - public int getTargetSdkVersion() { - return mTargetSdkVersion; - } - - /** - * @deprecated Use {@link #getHardwareConfig()} - */ - @Deprecated - public int getScreenWidth() { - return mHardwareConfig.getScreenWidth(); - } - - /** - * @deprecated Use {@link #getHardwareConfig()} - */ - @Deprecated - public int getScreenHeight() { - return mHardwareConfig.getScreenHeight(); - } - - /** - * @deprecated Use {@link #getHardwareConfig()} - */ - @Deprecated - public Density getDensity() { - return mHardwareConfig.getDensity(); - } - - /** - * @deprecated Use {@link #getHardwareConfig()} - */ - @Deprecated - public float getXdpi() { - return mHardwareConfig.getXdpi(); - } - - /** - * @deprecated Use {@link #getHardwareConfig()} - */ - @Deprecated - public float getYdpi() { - return mHardwareConfig.getYdpi(); - } - - public RenderResources getResources() { - return mRenderResources; - } - - public IProjectCallback getProjectCallback() { - return mProjectCallback; - } - - public LayoutLog getLog() { - return mLog; - } - - public boolean isBgColorOverridden() { - return mCustomBackgroundEnabled; - } - - public int getOverrideBgColor() { - return mCustomBackgroundColor; - } - - public long getTimeout() { - return mTimeout; - } - - public IImageFactory getImageFactory() { - return mImageFactory; - } - - /** - * @deprecated Use {@link #getHardwareConfig()} - */ - @Deprecated - public ScreenSize getConfigScreenSize() { - return mHardwareConfig.getScreenSize(); - } - - public String getAppIcon() { - return mAppIcon; - } - - public String getAppLabel() { - return mAppLabel; - } - - public String getLocale() { - return mLocale; - } - - public boolean isForceNoDecor() { - return mForceNoDecor; - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/RenderResources.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/RenderResources.java deleted file mode 100644 index c362224..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/RenderResources.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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.ide.common.rendering.api; - -import com.android.resources.ResourceType; - -/** - * A class containing all the resources needed to do a rendering. - * <p/> - * This contains both the project specific resources and the framework resources, and provide - * convenience methods to resolve resource and theme references. - */ -public class RenderResources { - - public final static String REFERENCE_NULL = "@null"; - - public static class FrameworkResourceIdProvider { - public Integer getId(ResourceType resType, String resName) { - return null; - } - } - - public void setFrameworkResourceIdProvider(FrameworkResourceIdProvider provider) { - } - - public void setLogger(LayoutLog logger) { - } - - /** - * Returns the {@link StyleResourceValue} representing the current theme. - * @return the theme or null if there is no current theme. - */ - public StyleResourceValue getCurrentTheme() { - return null; - } - - /** - * Returns a theme by its name. - * - * @param name the name of the theme - * @param frameworkTheme whether the theme is a framework theme. - * @return the theme or null if there's no match - */ - public StyleResourceValue getTheme(String name, boolean frameworkTheme) { - return null; - } - - /** - * Returns whether a theme is a parent of a given theme. - * @param parentTheme the parent theme - * @param childTheme the child theme. - * @return true if the parent theme is indeed a parent theme of the child theme. - */ - public boolean themeIsParentOf(StyleResourceValue parentTheme, StyleResourceValue childTheme) { - return false; - } - - /** - * Returns a framework resource by type and name. The returned resource is resolved. - * @param resourceType the type of the resource - * @param resourceName the name of the resource - */ - public ResourceValue getFrameworkResource(ResourceType resourceType, String resourceName) { - return null; - } - - /** - * Returns a project resource by type and name. The returned resource is resolved. - * @param resourceType the type of the resource - * @param resourceName the name of the resource - */ - public ResourceValue getProjectResource(ResourceType resourceType, String resourceName) { - return null; - } - - /** - * Returns the {@link ResourceValue} matching a given name in the current theme. If the - * item is not directly available in the theme, the method looks in its parent theme. - * - * @param itemName the name of the item to search for. - * @return the {@link ResourceValue} object or <code>null</code> - * - * @deprecated Use {@link #findItemInTheme(String, boolean)} - */ - @Deprecated - public ResourceValue findItemInTheme(String itemName) { - StyleResourceValue currentTheme = getCurrentTheme(); - if (currentTheme != null) { - return findItemInStyle(currentTheme, itemName); - } - - return null; - } - - /** - * Returns the {@link ResourceValue} matching a given attribute in the current theme. If the - * item is not directly available in the theme, the method looks in its parent theme. - * - * @param attrName the name of the attribute to search for. - * @param isFrameworkAttr whether the attribute is a framework attribute - * @return the {@link ResourceValue} object or <code>null</code> - */ - public ResourceValue findItemInTheme(String attrName, boolean isFrameworkAttr) { - StyleResourceValue currentTheme = getCurrentTheme(); - if (currentTheme != null) { - return findItemInStyle(currentTheme, attrName, isFrameworkAttr); - } - - return null; - } - - /** - * Returns the {@link ResourceValue} matching a given name in a given style. If the - * item is not directly available in the style, the method looks in its parent style. - * - * This version of doesn't support providing the namespace of the attribute so it'll search - * in both the project's namespace and then in the android namespace. - * - * @param style the style to search in - * @param attrName the name of the attribute to search for. - * @return the {@link ResourceValue} object or <code>null</code> - * - * @deprecated Use {@link #findItemInStyle(StyleResourceValue, String, boolean)} since this - * method doesn't know the item namespace. - */ - @Deprecated - public ResourceValue findItemInStyle(StyleResourceValue style, String attrName) { - return null; - } - - /** - * Returns the {@link ResourceValue} matching a given attribute in a given style. If the - * item is not directly available in the style, the method looks in its parent style. - * - * @param style the style to search in - * @param attrName the name of the attribute to search for. - * @param isFrameworkAttr whether the attribute is a framework attribute - * @return the {@link ResourceValue} object or <code>null</code> - */ - public ResourceValue findItemInStyle(StyleResourceValue style, String attrName, - boolean isFrameworkAttr) { - return null; - } - - /** - * Searches for, and returns a {@link ResourceValue} by its reference. - * <p/> - * The reference format can be: - * <pre>@resType/resName</pre> - * <pre>@android:resType/resName</pre> - * <pre>@resType/android:resName</pre> - * <pre>?resType/resName</pre> - * <pre>?android:resType/resName</pre> - * <pre>?resType/android:resName</pre> - * Any other string format will return <code>null</code>. - * <p/> - * The actual format of a reference is <pre>@[namespace:]resType/resName</pre> but this method - * only support the android namespace. - * - * @param reference the resource reference to search for. - * @param forceFrameworkOnly if true all references are considered to be toward framework - * resource even if the reference does not include the android: prefix. - * @return a {@link ResourceValue} or <code>null</code>. - */ - public ResourceValue findResValue(String reference, boolean forceFrameworkOnly) { - return null; - } - - /** - * Resolves the value of a resource, if the value references a theme or resource value. - * <p/> - * This method ensures that it returns a {@link ResourceValue} object that does not - * reference another resource. - * If the resource cannot be resolved, it returns <code>null</code>. - * <p/> - * If a value that does not need to be resolved is given, the method will return a new - * instance of {@link ResourceValue} that contains the input value. - * - * @param type the type of the resource - * @param name the name of the attribute containing this value. - * @param value the resource value, or reference to resolve - * @param isFrameworkValue whether the value is a framework value. - * - * @return the resolved resource value or <code>null</code> if it failed to resolve it. - */ - public ResourceValue resolveValue(ResourceType type, String name, String value, - boolean isFrameworkValue) { - return null; - } - - /** - * Returns the {@link ResourceValue} referenced by the value of <var>value</var>. - * <p/> - * This method ensures that it returns a {@link ResourceValue} object that does not - * reference another resource. - * If the resource cannot be resolved, it returns <code>null</code>. - * <p/> - * If a value that does not need to be resolved is given, the method will return the input - * value. - * - * @param value the value containing the reference to resolve. - * @return a {@link ResourceValue} object or <code>null</code> - */ - public ResourceValue resolveResValue(ResourceValue value) { - return null; - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/RenderSession.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/RenderSession.java deleted file mode 100644 index 96caa6a..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/RenderSession.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.api; - -import static com.android.ide.common.rendering.api.Result.Status.NOT_IMPLEMENTED; - -import com.android.ide.common.rendering.api.Result.Status; - -import java.awt.image.BufferedImage; -import java.util.List; -import java.util.Map; - -/** - * An object allowing interaction with an Android layout. - * - * This is returned by {@link Bridge#createSession(SessionParams)}. - * and can then be used for subsequent actions on the layout. - * - * @since 5 - * - */ -public class RenderSession { - - /** - * Returns the last operation result. - */ - public Result getResult() { - return NOT_IMPLEMENTED.createResult(); - } - - /** - * Returns the {@link ViewInfo} objects for the top level views. - * <p/> - * In most case the list will only contain one item. If the top level node is {@code merge} - * though then it will contain all the items under the {@code merge} tag. - * <p/> - * This is reset to a new instance every time {@link #render()} is called and can be - * <code>null</code> if the call failed (and the method returned a {@link Result} with - * {@link Status#ERROR_UNKNOWN} or {@link Status#NOT_IMPLEMENTED}. - * <p/> - * This can be safely modified by the caller. - * - * @return the list of {@link ViewInfo} or null if there aren't any. - */ - public List<ViewInfo> getRootViews() { - return null; - } - - /** - * Returns the rendering of the full layout. - * <p> - * This is reset to a new instance every time {@link #render()} is called and can be - * <code>null</code> if the call failed (and the method returned a {@link Result} with - * {@link Status#ERROR_UNKNOWN} or {@link Status#NOT_IMPLEMENTED}. - * <p/> - * This can be safely modified by the caller. - */ - public BufferedImage getImage() { - return null; - } - - /** - * Returns true if the current image alpha channel is relevant. - * - * @return whether the image alpha channel is relevant. - */ - public boolean isAlphaChannelImage() { - return true; - } - - /** - * Returns a map of (XML attribute name, attribute value) containing only default attribute - * values, for the given view Object. - * @param viewObject the view object. - * @return a map of the default property values or null. - */ - public Map<String, String> getDefaultProperties(Object viewObject) { - return null; - } - - /** - * Re-renders the layout as-is. - * In case of success, this should be followed by calls to {@link #getRootViews()} and - * {@link #getImage()} to access the result of the rendering. - * - * This is equivalent to calling <code>render(SceneParams.DEFAULT_TIMEOUT)</code> - * - * @return a {@link Result} indicating the status of the action. - */ - public Result render() { - return render(RenderParams.DEFAULT_TIMEOUT); - } - - /** - * Re-renders the layout as-is, with a given timeout in case other renderings are being done. - * In case of success, this should be followed by calls to {@link #getRootViews()} and - * {@link #getImage()} to access the result of the rendering. - * - * The {@link Bridge} is only able to inflate or render one layout at a time. There - * is an internal lock object whenever such an action occurs. The timeout parameter is used - * when attempting to acquire the lock. If the timeout expires, the method will return - * {@link Status#ERROR_TIMEOUT}. - * - * @param timeout timeout for the rendering, in milliseconds. - * - * @return a {@link Result} indicating the status of the action. - */ - public Result render(long timeout) { - return NOT_IMPLEMENTED.createResult(); - } - - /** - * Sets the value of a given property on a given object. - * <p/> - * This does nothing more than change the property. To render the scene in its new state, a - * call to {@link #render()} is required. - * <p/> - * Any amount of actions can be taken on the scene before {@link #render()} is called. - * - * @param objectView - * @param propertyName - * @param propertyValue - * - * @return a {@link Result} indicating the status of the action. - * - * @throws IllegalArgumentException if the view object is not an android.view.View - */ - public Result setProperty(Object objectView, String propertyName, String propertyValue) { - return NOT_IMPLEMENTED.createResult(); - } - - /** - * returns the value of a given property on a given object. - * <p/> - * This returns a {@link Result} object. If the operation of querying the object for its - * property was successful (check {@link Result#isSuccess()}), then the property value - * is set in the result and can be accessed through {@link Result#getData()}. - * - * @param objectView - * @param propertyName - * - * @return a {@link Result} indicating the status of the action. - * - * @throws IllegalArgumentException if the view object is not an android.view.View - */ - public Result getProperty(Object objectView, String propertyName) { - return NOT_IMPLEMENTED.createResult(); - } - - /** - * Inserts a new child in a ViewGroup object, and renders the result. - * <p/> - * The child is first inflated and then added to its new parent, at the given <var>index<var> - * position. If the <var>index</var> is -1 then the child is added at the end of the parent. - * <p/> - * If an animation listener is passed then the rendering is done asynchronously and the - * result is sent to the listener. - * If the listener is null, then the rendering is done synchronously. - * <p/> - * The child stays in the view hierarchy after the rendering is done. To remove it call - * {@link #removeChild(Object, IAnimationListener)} - * <p/> - * The returned {@link Result} object will contain the android.view.View object for - * the newly inflated child. It is accessible through {@link Result#getData()}. - * - * @param parentView the parent View object to receive the new child. - * @param childXml an {@link ILayoutPullParser} containing the content of the new child, - * including ViewGroup.LayoutParams attributes. - * @param index the index at which position to add the new child into the parent. -1 means at - * the end. - * @param listener an optional {@link IAnimationListener}. - * - * @return a {@link Result} indicating the status of the action. - */ - public Result insertChild(Object parentView, ILayoutPullParser childXml, int index, - IAnimationListener listener) { - return NOT_IMPLEMENTED.createResult(); - } - - /** - * Move a new child to a different ViewGroup object. - * <p/> - * The child is first removed from its current parent, and then added to its new parent, at the - * given <var>index<var> position. In case the <var>parentView</var> is the current parent of - * <var>childView</var> then the index must be the value with the <var>childView</var> removed - * from its parent. If the <var>index</var> is -1 then the child is added at the end of - * the parent. - * <p/> - * If an animation listener is passed then the rendering is done asynchronously and the - * result is sent to the listener. - * If the listener is null, then the rendering is done synchronously. - * <p/> - * The child stays in the view hierarchy after the rendering is done. To remove it call - * {@link #removeChild(Object, IAnimationListener)} - * <p/> - * The returned {@link Result} object will contain the android.view.ViewGroup.LayoutParams - * object created from the <var>layoutParams</var> map if it was non <code>null</code>. - * - * @param parentView the parent View object to receive the child. Can be the current parent - * already. - * @param childView the view to move. - * @param index the index at which position to add the new child into the parent. -1 means at - * the end. - * @param layoutParams an optional map of new ViewGroup.LayoutParams attribute. If non null, - * then the current layout params of the view will be removed and a new one will - * be inflated and set with the content of the map. - * @param listener an optional {@link IAnimationListener}. - * - * @return a {@link Result} indicating the status of the action. - */ - public Result moveChild(Object parentView, Object childView, int index, - Map<String, String> layoutParams, IAnimationListener listener) { - return NOT_IMPLEMENTED.createResult(); - } - - /** - * Removes a child from a ViewGroup object. - * <p/> - * This does nothing more than change the layout. To render the scene in its new state, a - * call to {@link #render()} is required. - * <p/> - * Any amount of actions can be taken on the scene before {@link #render()} is called. - * - * @param childView the view object to remove from its parent - * @param listener an optional {@link IAnimationListener}. - * - * @return a {@link Result} indicating the status of the action. - */ - public Result removeChild(Object childView, IAnimationListener listener) { - return NOT_IMPLEMENTED.createResult(); - } - - /** - * Starts playing an given animation on a given object. - * <p/> - * The animation playback is asynchronous and the rendered frame is sent vi the - * <var>listener</var>. - * - * @param targetObject the view object to animate - * @param animationName the name of the animation (res/anim) to play. - * @param listener the listener callback. - * - * @return a {@link Result} indicating the status of the action. - */ - public Result animate(Object targetObject, String animationName, - boolean isFrameworkAnimation, IAnimationListener listener) { - return NOT_IMPLEMENTED.createResult(); - } - - /** - * Discards the layout. No more actions can be called on this object. - */ - public void dispose() { - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ResourceReference.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ResourceReference.java deleted file mode 100644 index f22f51e..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ResourceReference.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.ide.common.rendering.api; - -/** - * A resource reference. This contains the String ID of the resource and whether this is a framework - * reference. - * This is an immutable class. - * - */ -public class ResourceReference { - private final String mName; - private final boolean mIsFramework; - - /** - * Builds a resource reference. - * @param name the name of the resource - * @param isFramework whether the reference is to a framework resource. - */ - public ResourceReference(String name, boolean isFramework) { - mName = name; - mIsFramework = isFramework; - } - - /** - * Builds a non-framework resource reference. - * @param name the name of the resource - */ - public ResourceReference(String name) { - this(name, false /*platformLayout*/); - } - - /** - * Returns the name of the resource, as defined in the XML. - */ - public final String getName() { - return mName; - } - - /** - * Returns whether the resource is a framework resource (<code>true</code>) or a project - * resource (<code>false</false>). - */ - public final boolean isFramework() { - return mIsFramework; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (mIsFramework ? 1231 : 1237); - result = prime * result + ((mName == null) ? 0 : mName.hashCode()); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ResourceReference other = (ResourceReference) obj; - if (mIsFramework != other.mIsFramework) - return false; - if (mName == null) { - if (other.mName != null) - return false; - } else if (!mName.equals(other.mName)) - return false; - return true; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "ResourceReference [" + mName + " (framework:" + mIsFramework+ ")]"; - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ResourceValue.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ResourceValue.java deleted file mode 100644 index dceb7c5..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ResourceValue.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.rendering.api; - -import com.android.layoutlib.api.IResourceValue; -import com.android.resources.ResourceType; - -/** - * Represents an android resource with a name and a string value. - */ -@SuppressWarnings("deprecation") -public class ResourceValue extends ResourceReference implements IResourceValue { - private final ResourceType mType; - private String mValue = null; - - public ResourceValue(ResourceType type, String name, boolean isFramework) { - super(name, isFramework); - mType = type; - } - - public ResourceValue(ResourceType type, String name, String value, boolean isFramework) { - super(name, isFramework); - mType = type; - mValue = value; - } - - public ResourceType getResourceType() { - return mType; - } - - /** - * Returns the type of the resource. For instance "drawable", "color", etc... - * @deprecated use {@link #getResourceType()} instead. - */ - @Override - @Deprecated - public String getType() { - return mType.getName(); - } - - /** - * Returns the value of the resource, as defined in the XML. This can be <code>null</code> - */ - @Override - public final String getValue() { - return mValue; - } - - /** - * Sets the value of the resource. - * @param value the new value - */ - public void setValue(String value) { - mValue = value; - } - - /** - * Sets the value from another resource. - * @param value the resource value - */ - public void replaceWith(ResourceValue value) { - mValue = value.mValue; - } - - @Override - public String toString() { - return "ResourceValue [" + mType + "/" + getName() + " = " + mValue //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - + " (framework:" + isFramework() + ")]"; //$NON-NLS-1$ //$NON-NLS-2$ - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((mType == null) ? 0 : mType.hashCode()); - result = prime * result + ((mValue == null) ? 0 : mValue.hashCode()); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - ResourceValue other = (ResourceValue) obj; - if (mType == null) { - if (other.mType != null) - return false; - } else if (!mType.equals(other.mType)) - return false; - if (mValue == null) { - if (other.mValue != null) - return false; - } else if (!mValue.equals(other.mValue)) - return false; - return true; - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/Result.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/Result.java deleted file mode 100644 index a739e79..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/Result.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.api; - -/** - * Scene result class. This is an immutable class. - * <p/> - * This cannot be allocated directly, instead use - * {@link Status#createResult()}, - * {@link Status#createResult(String, Throwable)}, - * {@link Status#createResult(String)} - * {@link Status#createResult(Object)} - */ -public class Result { - - private final Status mStatus; - private final String mErrorMessage; - private final Throwable mThrowable; - private Object mData; - - /** - * Scene Status enum. - * <p/>This indicates the status of all scene actions. - */ - public enum Status { - SUCCESS, - NOT_IMPLEMENTED, - ERROR_TIMEOUT, - ERROR_LOCK_INTERRUPTED, - ERROR_INFLATION, - ERROR_VIEWGROUP_NO_CHILDREN, - ERROR_NOT_INFLATED, - ERROR_RENDER, - ERROR_ANIM_NOT_FOUND, - ERROR_NOT_A_DRAWABLE, - ERROR_REFLECTION, - ERROR_UNKNOWN; - - private Result mResult; - - /** - * Returns a {@link Result} object with this status. - * @return an instance of SceneResult; - */ - public Result createResult() { - // don't want to get generic error that way. - assert this != ERROR_UNKNOWN; - - if (mResult == null) { - mResult = new Result(this); - } - - return mResult; - } - - /** - * Returns a {@link Result} object with this status, and the given data. - * @return an instance of SceneResult; - * - * @see Result#getData() - */ - public Result createResult(Object data) { - Result res = createResult(); - - if (data != null) { - res = res.getCopyWithData(data); - } - - return res; - } - - /** - * Returns a {@link #ERROR_UNKNOWN} result with the given message and throwable - * @param errorMessage the error message - * @param throwable the throwable - * @return an instance of SceneResult. - */ - public Result createResult(String errorMessage, Throwable throwable) { - return new Result(this, errorMessage, throwable); - } - - /** - * Returns a {@link #ERROR_UNKNOWN} result with the given message - * @param errorMessage the error message - * @return an instance of SceneResult. - */ - public Result createResult(String errorMessage) { - return new Result(this, errorMessage, null /*throwable*/); - } - } - - /** - * Creates a {@link Result} object with the given SceneStatus. - * - * @param status the status. Must not be null. - */ - private Result(Status status) { - this(status, null, null); - } - - /** - * Creates a {@link Result} object with the given SceneStatus, and the given message - * and {@link Throwable} - * - * @param status the status. Must not be null. - * @param errorMessage an optional error message. - * @param t an optional exception. - */ - private Result(Status status, String errorMessage, Throwable t) { - assert status != null; - mStatus = status; - mErrorMessage = errorMessage; - mThrowable = t; - } - - private Result(Result result) { - mStatus = result.mStatus; - mErrorMessage = result.mErrorMessage; - mThrowable = result.mThrowable; - } - - /** - * Returns a copy of the current result with the added (or replaced) given data - * @param data the data bundle - * - * @return returns a new SceneResult instance. - */ - public Result getCopyWithData(Object data) { - Result r = new Result(this); - r.mData = data; - return r; - } - - - /** - * Returns whether the status is successful. - * <p> - * This is the same as calling <code>getStatus() == SceneStatus.SUCCESS</code> - * @return <code>true</code> if the status is successful. - */ - public boolean isSuccess() { - return mStatus == Status.SUCCESS; - } - - /** - * Returns the status. This is never null. - */ - public Status getStatus() { - return mStatus; - } - - /** - * Returns the error message. This is only non-null when {@link #getStatus()} returns - * {@link Status#ERROR_UNKNOWN} - */ - public String getErrorMessage() { - return mErrorMessage; - } - - /** - * Returns the exception. This is only non-null when {@link #getStatus()} returns - * {@link Status#ERROR_UNKNOWN} - */ - public Throwable getException() { - return mThrowable; - } - - /** - * Returns the optional data bundle stored in the result object. - * @return the data bundle or <code>null</code> if none have been set. - */ - public Object getData() { - return mData; - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/SessionParams.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/SessionParams.java deleted file mode 100644 index 709207e..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/SessionParams.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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.ide.common.rendering.api; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Rendering parameters for a {@link RenderSession}. - */ -public class SessionParams extends RenderParams { - - public static enum RenderingMode { - NORMAL(false, false), - V_SCROLL(false, true), - H_SCROLL(true, false), - FULL_EXPAND(true, true); - - private final boolean mHorizExpand; - private final boolean mVertExpand; - - private RenderingMode(boolean horizExpand, boolean vertExpand) { - mHorizExpand = horizExpand; - mVertExpand = vertExpand; - } - - public boolean isHorizExpand() { - return mHorizExpand; - } - - public boolean isVertExpand() { - return mVertExpand; - } - } - - private final ILayoutPullParser mLayoutDescription; - private final RenderingMode mRenderingMode; - private boolean mLayoutOnly = false; - private Map<ResourceReference, AdapterBinding> mAdapterBindingMap; - private boolean mExtendedViewInfoMode = false; - - /** - * - * @param layoutDescription the {@link ILayoutPullParser} letting the LayoutLib Bridge visit the - * layout file. - * @param renderingMode The rendering mode. - * @param projectKey An Object identifying the project. This is used for the cache mechanism. - * @param hardwareConfig the {@link HardwareConfig}. - * @param renderResources a {@link RenderResources} object providing access to the resources. - * @param projectCallback The {@link IProjectCallback} object to get information from - * the project. - * @param minSdkVersion the minSdkVersion of the project - * @param targetSdkVersion the targetSdkVersion of the project - * @param log the object responsible for displaying warning/errors to the user. - */ - public SessionParams( - ILayoutPullParser layoutDescription, - RenderingMode renderingMode, - Object projectKey, - HardwareConfig hardwareConfig, - RenderResources renderResources, - IProjectCallback projectCallback, - int minSdkVersion, int targetSdkVersion, - LayoutLog log) { - super(projectKey, hardwareConfig, - renderResources, projectCallback, minSdkVersion, targetSdkVersion, log); - - mLayoutDescription = layoutDescription; - mRenderingMode = renderingMode; - } - - public SessionParams(SessionParams params) { - super(params); - mLayoutDescription = params.mLayoutDescription; - mRenderingMode = params.mRenderingMode; - if (params.mAdapterBindingMap != null) { - mAdapterBindingMap = new HashMap<ResourceReference, AdapterBinding>( - params.mAdapterBindingMap); - } - mExtendedViewInfoMode = params.mExtendedViewInfoMode; - } - - public ILayoutPullParser getLayoutDescription() { - return mLayoutDescription; - } - - public RenderingMode getRenderingMode() { - return mRenderingMode; - } - - public void setLayoutOnly() { - mLayoutOnly = true; - } - - public boolean isLayoutOnly() { - return mLayoutOnly; - } - - public void addAdapterBinding(ResourceReference reference, AdapterBinding data) { - if (mAdapterBindingMap == null) { - mAdapterBindingMap = new HashMap<ResourceReference, AdapterBinding>(); - } - - mAdapterBindingMap.put(reference, data); - } - - public Map<ResourceReference, AdapterBinding> getAdapterBindings() { - if (mAdapterBindingMap == null) { - return Collections.emptyMap(); - } - - return Collections.unmodifiableMap(mAdapterBindingMap); - } - - public void setExtendedViewInfoMode(boolean mode) { - mExtendedViewInfoMode = mode; - } - - public boolean getExtendedViewInfoMode() { - return mExtendedViewInfoMode; - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/StyleResourceValue.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/StyleResourceValue.java deleted file mode 100644 index 7fdfd6a..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/StyleResourceValue.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.rendering.api; - -import com.android.layoutlib.api.IResourceValue; -import com.android.layoutlib.api.IStyleResourceValue; -import com.android.resources.ResourceType; -import com.android.util.Pair; - -import java.util.HashMap; - -/** - * Represents an android style resources with a name and a list of children {@link ResourceValue}. - */ -@SuppressWarnings("deprecation") -public final class StyleResourceValue extends ResourceValue implements IStyleResourceValue { - - private String mParentStyle = null; - private HashMap<Pair<String, Boolean>, ResourceValue> mItems = new HashMap<Pair<String, Boolean>, ResourceValue>(); - - public StyleResourceValue(ResourceType type, String name, boolean isFramework) { - super(type, name, isFramework); - } - - public StyleResourceValue(ResourceType type, String name, String parentStyle, - boolean isFramework) { - super(type, name, isFramework); - mParentStyle = parentStyle; - } - - /** - * Returns the parent style name or <code>null</code> if unknown. - */ - @Override - public String getParentStyle() { - return mParentStyle; - } - - /** - * Finds a value in the list by name - * @param name the name of the resource - * - * @deprecated use {@link #findValue(String, boolean)} - */ - @Deprecated - public ResourceValue findValue(String name) { - return mItems.get(Pair.of(name, isFramework())); - } - - /** - * Finds a value in the list by name - * @param name the name of the resource - */ - public ResourceValue findValue(String name, boolean isFrameworkAttr) { - return mItems.get(Pair.of(name, isFrameworkAttr)); - } - - public void addValue(ResourceValue value, boolean isFrameworkAttr) { - mItems.put(Pair.of(value.getName(), isFrameworkAttr), value); - } - - @Override - public void replaceWith(ResourceValue value) { - assert value instanceof StyleResourceValue; - super.replaceWith(value); - - if (value instanceof StyleResourceValue) { - mItems.clear(); - mItems.putAll(((StyleResourceValue)value).mItems); - } - } - - /** - * Legacy method. - * @deprecated use {@link #getValue()} - */ - @Override - @Deprecated - public IResourceValue findItem(String name) { - return mItems.get(name); - } -} diff --git a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ViewInfo.java b/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ViewInfo.java deleted file mode 100644 index d859e95..0000000 --- a/layoutlib_api/src/main/java/com/android/ide/common/rendering/api/ViewInfo.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.api; - -import java.util.Collections; -import java.util.List; - -/** - * Layout information for a specific view object - */ -public class ViewInfo { - - private final Object mCookie; - private final String mName; - private final int mLeft; - private final int mRight; - private final int mTop; - private final int mBottom; - private List<ViewInfo> mChildren = Collections.emptyList(); - private final Object mViewObject; - private final Object mLayoutParamsObject; - - // optional info - private int mBaseLine = Integer.MIN_VALUE; - private int mLeftMargin = Integer.MIN_VALUE; - private int mTopMargin = Integer.MIN_VALUE; - private int mRightMargin = Integer.MIN_VALUE; - private int mBottomMargin = Integer.MIN_VALUE; - - public ViewInfo(String name, Object cookie, int left, int top, int right, int bottom) { - this(name, cookie, left, top, right, bottom, null /*viewObject*/, - null /*layoutParamsObject*/); - } - - public ViewInfo(String name, Object cookie, int left, int top, int right, int bottom, - Object viewObject, Object layoutParamsObject) { - mName = name; - mCookie = cookie; - mLeft = left; - mRight = right; - mTop = top; - mBottom = bottom; - mViewObject = viewObject; - mLayoutParamsObject = layoutParamsObject; - } - - /** - * Sets the list of children {@link ViewInfo}. - */ - public void setChildren(List<ViewInfo> children) { - if (children != null) { - mChildren = Collections.unmodifiableList(children); - } else { - mChildren = Collections.emptyList(); - } - } - - public void setExtendedInfo(int baseLine, int leftMargin, int topMargin, - int rightMargin, int bottomMargin) { - mBaseLine = baseLine; - mLeftMargin = leftMargin; - mTopMargin = topMargin; - mRightMargin = rightMargin; - mBottomMargin = bottomMargin; - } - - /** - * Returns the list of children views. This is never null, but can be empty. - */ - public List<ViewInfo> getChildren() { - return mChildren; - } - - /** - * Returns the cookie associated with the XML node. Can be null. - * - * @see ILayoutPullParser#getViewCookie() - */ - public Object getCookie() { - return mCookie; - } - - /** - * Returns the class name of the view object. Can be null. - */ - public String getClassName() { - return mName; - } - - /** - * Returns the left of the view bounds, relative to the view parent bounds. - */ - public int getLeft() { - return mLeft; - } - - /** - * Returns the top of the view bounds, relative to the view parent bounds. - */ - public int getTop() { - return mTop; - } - - /** - * Returns the right of the view bounds, relative to the view parent bounds. - */ - public int getRight() { - return mRight; - } - - /** - * Returns the bottom of the view bounds, relative to the view parent bounds. - */ - public int getBottom() { - return mBottom; - } - - /** - * Returns the actual android.view.View (or child class) object. This can be used - * to query the object properties that are not in the XML and not available through - * {@link RenderSession#getProperty(Object, String)}. - */ - public Object getViewObject() { - return mViewObject; - } - - /** - * Returns the actual android.view.ViewGroup$LayoutParams (or child class) object. - * This can be used to query the object properties that are not in the XML and not available - * through {@link RenderSession#getProperty(Object, String)}. - */ - public Object getLayoutParamsObject() { - return mLayoutParamsObject; - } - - /** - * Returns the baseline value. If the value is unknown, returns {@link Integer#MIN_VALUE}. - */ - public int getBaseLine() { - return mBaseLine; - } - - /** - * Returns the left margin value. If the value is unknown, returns {@link Integer#MIN_VALUE}. - */ - public int getLeftMargin() { - return mLeftMargin; - } - - /** - * Returns the top margin value. If the value is unknown, returns {@link Integer#MIN_VALUE}. - */ - public int getTopMargin() { - return mTopMargin; - } - - /** - * Returns the right margin value. If the value is unknown, returns {@link Integer#MIN_VALUE}. - */ - public int getRightMargin() { - return mRightMargin; - } - - /** - * Returns the bottom margin value. If the value is unknown, returns {@link Integer#MIN_VALUE}. - */ - public int getBottomMargin() { - return mBottomMargin; - } -} diff --git a/layoutlib_api/src/main/java/com/android/layoutlib/api/IDensityBasedResourceValue.java b/layoutlib_api/src/main/java/com/android/layoutlib/api/IDensityBasedResourceValue.java deleted file mode 100644 index 25d9bfb..0000000 --- a/layoutlib_api/src/main/java/com/android/layoutlib/api/IDensityBasedResourceValue.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2008 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.layoutlib.api; - -import com.android.ide.common.rendering.api.DensityBasedResourceValue; - -/** - * Represents an Android Resources that has a density info attached to it. - * @deprecated use {@link DensityBasedResourceValue}. - */ -@Deprecated -public interface IDensityBasedResourceValue extends IResourceValue { - - /** - * Density. - * - * @deprecated use {@link com.android.resources.Density}. - */ - @Deprecated - public static enum Density { - XHIGH(320), - HIGH(240), - MEDIUM(160), - LOW(120), - NODPI(0); - - private final int mValue; - - Density(int value) { - mValue = value; - } - - public int getValue() { - return mValue; - } - - /** - * Returns the enum matching the given density value - * @param value The density value. - * @return the enum for the density value or null if no match was found. - */ - public static Density getEnum(int value) { - for (Density d : values()) { - if (d.mValue == value) { - return d; - } - } - - return null; - } - } - - /** - * Returns the density associated to the resource. - * @deprecated use {@link DensityBasedResourceValue#getResourceDensity()} - */ - @Deprecated - Density getDensity(); -} diff --git a/layoutlib_api/src/main/java/com/android/layoutlib/api/ILayoutBridge.java b/layoutlib_api/src/main/java/com/android/layoutlib/api/ILayoutBridge.java deleted file mode 100644 index f849cdd..0000000 --- a/layoutlib_api/src/main/java/com/android/layoutlib/api/ILayoutBridge.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2008 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.layoutlib.api; - -import com.android.ide.common.rendering.api.Bridge; - -import java.util.Map; - -/** - * Entry point of the Layout Lib. Implementations of this interface provide a method to compute - * and render a layout. - * <p/> - * <p/>{@link #getApiLevel()} gives the ability to know which methods are available. - * <p/> - * Changes in API level 5: - * <ul> - * <li>Bridge should extend {@link Bridge} instead of implementing {@link ILayoutBridge}.</li> - * </ul> - * Changes in API level 4: - * <ul> - * <li>new render method: {@link #computeLayout(IXmlPullParser, Object, int, int, boolean, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li> - * <li>deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li> - * </ul> - * Changes in API level 3: - * <ul> - * <li>new render method: {@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li> - * <li>deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li> - * </ul> - * Changes in API level 2: - * <ul> - * <li>new API Level method: {@link #getApiLevel()}</li> - * <li>new render method: {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li> - * <li>deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, Map, Map, IProjectCallback, ILayoutLog)}</li> - * </ul> - * @deprecated Extend {@link Bridge} instead. - */ -@Deprecated -public interface ILayoutBridge { - - final int API_CURRENT = 4; - - /** - * Returns the API level of the layout library. - * While no methods will ever be removed, some may become deprecated, and some new ones - * will appear. - * <p/>If calling this method throws an {@link AbstractMethodError}, then the API level - * should be considered to be 1. - */ - int getApiLevel(); - - /** - * Initializes the Bridge object. - * @param fontOsLocation the location of the fonts. - * @param enumValueMap map attrName => { map enumFlagName => Integer value }. - * @return true if success. - * @since 1 - */ - boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap); - - /** - * Prepares the layoutlib to unloaded. - */ - boolean dispose(); - - /** - * Starts a layout session by inflating and rendering it. The method returns a - * {@link ILayoutResult} on which further actions can be taken. - * - * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the - * layout file. - * @param projectKey An Object identifying the project. This is used for the cache mechanism. - * @param screenWidth the screen width - * @param screenHeight the screen height - * @param renderFullSize if true, the rendering will render the full size needed by the - * layout. This size is never smaller than <var>screenWidth</var> x <var>screenHeight</var>. - * @param density the density factor for the screen. - * @param xdpi the screen actual dpi in X - * @param ydpi the screen actual dpi in Y - * @param themeName The name of the theme to use. - * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme. - * @param projectResources the resources of the project. The map contains (String, map) pairs - * where the string is the type of the resource reference used in the layout file, and the - * map contains (String, {@link IResourceValue}) pairs where the key is the resource name, - * and the value is the resource value. - * @param frameworkResources the framework resources. The map contains (String, map) pairs - * where the string is the type of the resource reference used in the layout file, and the map - * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the - * value is the resource value. - * @param projectCallback The {@link IProjectCallback} object to get information from - * the project. - * @param logger the object responsible for displaying warning/errors to the user. - * @return a new {@link ILayoutResult} object that contains the result of the layout. - * @deprecated use {@link Bridge#createSession(com.android.ide.common.rendering.api.SessionParams)} - * @since 4 - */ - @Deprecated - ILayoutResult computeLayout(IXmlPullParser layoutDescription, - Object projectKey, - int screenWidth, int screenHeight, boolean renderFullSize, - int density, float xdpi, float ydpi, - String themeName, boolean isProjectTheme, - Map<String, Map<String, IResourceValue>> projectResources, - Map<String, Map<String, IResourceValue>> frameworkResources, - IProjectCallback projectCallback, ILayoutLog logger); - - /** - * Computes and renders a layout - * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the - * layout file. - * @param projectKey An Object identifying the project. This is used for the cache mechanism. - * @param screenWidth the screen width - * @param screenHeight the screen height - * @param density the density factor for the screen. - * @param xdpi the screen actual dpi in X - * @param ydpi the screen actual dpi in Y - * @param themeName The name of the theme to use. - * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme. - * @param projectResources the resources of the project. The map contains (String, map) pairs - * where the string is the type of the resource reference used in the layout file, and the - * map contains (String, {@link IResourceValue}) pairs where the key is the resource name, - * and the value is the resource value. - * @param frameworkResources the framework resources. The map contains (String, map) pairs - * where the string is the type of the resource reference used in the layout file, and the map - * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the - * value is the resource value. - * @param projectCallback The {@link IProjectCallback} object to get information from - * the project. - * @param logger the object responsible for displaying warning/errors to the user. - * @return a new {@link ILayoutResult} object that contains the result of the layout. - * @deprecated use {@link Bridge#createSession(com.android.ide.common.rendering.api.SessionParams)} - * @since 3 - */ - @Deprecated - ILayoutResult computeLayout(IXmlPullParser layoutDescription, - Object projectKey, - int screenWidth, int screenHeight, int density, float xdpi, float ydpi, - String themeName, boolean isProjectTheme, - Map<String, Map<String, IResourceValue>> projectResources, - Map<String, Map<String, IResourceValue>> frameworkResources, - IProjectCallback projectCallback, ILayoutLog logger); - - /** - * Computes and renders a layout - * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the - * layout file. - * @param projectKey An Object identifying the project. This is used for the cache mechanism. - * @param screenWidth the screen width - * @param screenHeight the screen height - * @param themeName The name of the theme to use. - * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme. - * @param projectResources the resources of the project. The map contains (String, map) pairs - * where the string is the type of the resource reference used in the layout file, and the - * map contains (String, {@link IResourceValue}) pairs where the key is the resource name, - * and the value is the resource value. - * @param frameworkResources the framework resources. The map contains (String, map) pairs - * where the string is the type of the resource reference used in the layout file, and the map - * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the - * value is the resource value. - * @param projectCallback The {@link IProjectCallback} object to get information from - * the project. - * @param logger the object responsible for displaying warning/errors to the user. - * @return a new {@link ILayoutResult} object that contains the result of the layout. - * @deprecated use {@link Bridge#createSession(com.android.ide.common.rendering.api.SessionParams)} - * @since 2 - */ - @Deprecated - ILayoutResult computeLayout(IXmlPullParser layoutDescription, - Object projectKey, - int screenWidth, int screenHeight, String themeName, boolean isProjectTheme, - Map<String, Map<String, IResourceValue>> projectResources, - Map<String, Map<String, IResourceValue>> frameworkResources, - IProjectCallback projectCallback, ILayoutLog logger); - - /** - * Computes and renders a layout - * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the - * layout file. - * @param projectKey An Object identifying the project. This is used for the cache mechanism. - * @param screenWidth - * @param screenHeight - * @param themeName The name of the theme to use. In order to differentiate project and platform - * themes sharing the same name, all project themes must be prepended with a '*' character. - * @param projectResources the resources of the project. The map contains (String, map) pairs - * where the string is the type of the resource reference used in the layout file, and the - * map contains (String, {@link IResourceValue}) pairs where the key is the resource name, - * and the value is the resource value. - * @param frameworkResources the framework resources. The map contains (String, map) pairs - * where the string is the type of the resource reference used in the layout file, and the map - * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the - * value is the resource value. - * @param projectCallback The {@link IProjectCallback} object to get information from - * the project. - * @param logger the object responsible for displaying warning/errors to the user. - * @return a new {@link ILayoutResult} object that contains the result of the layout. - * @deprecated use {@link Bridge#createSession(com.android.ide.common.rendering.api.SessionParams)} - * @since 1 - */ - @Deprecated - ILayoutResult computeLayout(IXmlPullParser layoutDescription, - Object projectKey, - int screenWidth, int screenHeight, String themeName, - Map<String, Map<String, IResourceValue>> projectResources, - Map<String, Map<String, IResourceValue>> frameworkResources, - IProjectCallback projectCallback, ILayoutLog logger); - - /** - * Clears the resource cache for a specific project. - * <p/>This cache contains bitmaps and nine patches that are loaded from the disk and reused - * until this method is called. - * <p/>The cache is not configuration dependent and should only be cleared when a - * resource changes (at this time only bitmaps and 9 patches go into the cache). - * @param projectKey the key for the project. - * @since 1 - */ - void clearCaches(Object projectKey); -} diff --git a/layoutlib_api/src/main/java/com/android/layoutlib/api/ILayoutLog.java b/layoutlib_api/src/main/java/com/android/layoutlib/api/ILayoutLog.java deleted file mode 100644 index 38a22b8..0000000 --- a/layoutlib_api/src/main/java/com/android/layoutlib/api/ILayoutLog.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2008 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.layoutlib.api; - -import com.android.ide.common.rendering.api.LayoutLog; - -/** - * Callback interface to display warnings/errors that happened during the computation and - * rendering of the layout. - * @deprecated use {@link LayoutLog}. - */ -@Deprecated -public interface ILayoutLog { - - /** - * Logs a warning message. - * @param message the message to log. - */ - void warning(String message); - - /** - * Logs an error message. - * @param message the message to log. - */ - void error(String message); - - /** - * Logs an exception - * @param t the {@link Throwable} to log. - */ - void error(Throwable t); -} diff --git a/layoutlib_api/src/main/java/com/android/layoutlib/api/ILayoutResult.java b/layoutlib_api/src/main/java/com/android/layoutlib/api/ILayoutResult.java deleted file mode 100644 index 6aeaf9f..0000000 --- a/layoutlib_api/src/main/java/com/android/layoutlib/api/ILayoutResult.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2008 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.layoutlib.api; - -import com.android.ide.common.rendering.api.Bridge; -import com.android.ide.common.rendering.api.RenderSession; - -import java.awt.image.BufferedImage; - -/** - * The result of a layout computation through {@link ILayoutBridge}. - * - * @since 1 - * @deprecated use {@link RenderSession} as returned by {@link Bridge#createSession(com.android.ide.common.rendering.api.SessionParams)} - */ -@Deprecated -public interface ILayoutResult { - /** - * Success return code - */ - final static int SUCCESS = 0; - - /** - * Error return code, in which case an error message is guaranteed to be defined. - * @see #getErrorMessage() - */ - final static int ERROR = 1; - - /** - * Returns the result code. - * @see #SUCCESS - * @see #ERROR - */ - int getSuccess(); - - /** - * Returns the {@link ILayoutViewInfo} object for the top level view. - */ - ILayoutViewInfo getRootView(); - - /** - * Returns the rendering of the full layout. - */ - BufferedImage getImage(); - - /** - * Returns the error message. - * <p/>Only valid when {@link #getSuccess()} returns {@link #ERROR} - */ - String getErrorMessage(); - - /** - * Layout information for a specific view. - * @deprecated - */ - @Deprecated - public interface ILayoutViewInfo { - - /** - * Returns the list of children views. - */ - ILayoutViewInfo[] getChildren(); - - /** - * Returns the key associated with the node. - * @see IXmlPullParser#getViewKey() - */ - Object getViewKey(); - - /** - * Returns the name of the view. - */ - String getName(); - - /** - * Returns the left of the view bounds. - */ - int getLeft(); - - /** - * Returns the top of the view bounds. - */ - int getTop(); - - /** - * Returns the right of the view bounds. - */ - int getRight(); - - /** - * Returns the bottom of the view bounds. - */ - int getBottom(); - } -} diff --git a/layoutlib_api/src/main/java/com/android/layoutlib/api/IProjectCallback.java b/layoutlib_api/src/main/java/com/android/layoutlib/api/IProjectCallback.java deleted file mode 100644 index 65e2a87..0000000 --- a/layoutlib_api/src/main/java/com/android/layoutlib/api/IProjectCallback.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2010 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.layoutlib.api; - -/** - * - * @deprecated - * - */ -public interface IProjectCallback { - - /** - * Loads a custom view with the given constructor signature and arguments. - * @param name The fully qualified name of the class. - * @param constructorSignature The signature of the class to use - * @param constructorArgs The arguments to use on the constructor - * @return A newly instantiated android.view.View object. - * @throws ClassNotFoundException - * @throws Exception - */ - @SuppressWarnings("unchecked") - Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs) - throws ClassNotFoundException, Exception; - - /** - * Returns the namespace of the application. - * <p/>This lets the Layout Lib load custom attributes for custom views. - */ - String getNamespace(); - - /** - * Resolves the id of a resource Id. - * <p/>The resource id is the value of a <code>R.<type>.<name></code>, and - * this method will return both the type and name of the resource. - * @param id the Id to resolve. - * @return an array of 2 strings containing the resource name and type, or null if the id - * does not match any resource. - */ - String[] resolveResourceValue(int id); - - /** - * Resolves the id of a resource Id of type int[] - * <p/>The resource id is the value of a R.styleable.<name>, and this method will - * return the name of the resource. - * @param id the Id to resolve. - * @return the name of the resource or <code>null</code> if not found. - */ - String resolveResourceValue(int[] id); - - /** - * Returns the id of a resource. - * <p/>The provided type and name must match an existing constant defined as - * <code>R.<type>.<name></code>. - * @param type the type of the resource - * @param name the name of the resource - * @return an Integer containing the resource Id, or <code>null</code> if not found. - */ - Integer getResourceValue(String type, String name); -} diff --git a/layoutlib_api/src/main/java/com/android/layoutlib/api/IResourceValue.java b/layoutlib_api/src/main/java/com/android/layoutlib/api/IResourceValue.java deleted file mode 100644 index 9a00801..0000000 --- a/layoutlib_api/src/main/java/com/android/layoutlib/api/IResourceValue.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2008 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.layoutlib.api; - -import com.android.ide.common.rendering.api.ResourceValue; - -/** - * Represents an android resource with a name and a string value. - * @deprecated use {@link ResourceValue}. - */ -@Deprecated -public interface IResourceValue { - - /** - * Returns the type of the resource. For instance "drawable", "color", etc... - */ - String getType(); - - /** - * Returns the name of the resource, as defined in the XML. - */ - String getName(); - - /** - * Returns the value of the resource, as defined in the XML. This can be <code>null</code> - */ - String getValue(); - - /** - * Returns whether the resource is a framework resource (<code>true</code>) or a project - * resource (<code>false</false>). - */ - boolean isFramework(); -} diff --git a/layoutlib_api/src/main/java/com/android/layoutlib/api/IStyleResourceValue.java b/layoutlib_api/src/main/java/com/android/layoutlib/api/IStyleResourceValue.java deleted file mode 100644 index 21036ca..0000000 --- a/layoutlib_api/src/main/java/com/android/layoutlib/api/IStyleResourceValue.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2008 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.layoutlib.api; - -import com.android.ide.common.rendering.api.StyleResourceValue; - -/** - * Represents an android style resources with a name and a list of children {@link IResourceValue}. - * @deprecated Use {@link StyleResourceValue}. - */ -@Deprecated -public interface IStyleResourceValue extends IResourceValue { - - /** - * Returns the parent style name or <code>null</code> if unknown. - */ - String getParentStyle(); - - /** - * Find an item in the list by name - * @param name the name of the resource - * - * @deprecated use {@link StyleResourceValue#findValue(String)} - */ - IResourceValue findItem(String name); -} diff --git a/layoutlib_api/src/main/java/com/android/layoutlib/api/IXmlPullParser.java b/layoutlib_api/src/main/java/com/android/layoutlib/api/IXmlPullParser.java deleted file mode 100644 index e0a98a6..0000000 --- a/layoutlib_api/src/main/java/com/android/layoutlib/api/IXmlPullParser.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2008 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.layoutlib.api; - -import org.xmlpull.v1.XmlPullParser; - -/** - * @deprecated - */ -@Deprecated -public interface IXmlPullParser extends XmlPullParser { - - /** - * Returns a key for the current XML node. - * <p/>This key will be passed back in the {@link com.android.ide.common.rendering.api.ViewInfo} - * objects, allowing association of a particular XML node with its result from the - * layout computation. - */ - Object getViewKey(); -} diff --git a/layoutlib_api/src/main/java/com/android/resources/Density.java b/layoutlib_api/src/main/java/com/android/resources/Density.java deleted file mode 100644 index 1f3fb52..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/Density.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - - -/** - * Density enum. - * <p/>This is used in the manifest in the uses-configuration node and in the resource folder names - * as well as other places needing to know the density values. - */ -public enum Density implements ResourceEnum { - XXHIGH("xxhdpi", "XX-High Density", 480, 16), //$NON-NLS-1$ - XHIGH("xhdpi", "X-High Density", 320, 8), //$NON-NLS-1$ - HIGH("hdpi", "High Density", 240, 4), //$NON-NLS-1$ - TV("tvdpi", "TV Density", 213, 13), //$NON-NLS-1$ - MEDIUM("mdpi", "Medium Density", 160, 4), //$NON-NLS-1$ - LOW("ldpi", "Low Density", 120, 4), //$NON-NLS-1$ - NODPI("nodpi", "No Density", 0, 4); //$NON-NLS-1$ - - public final static int DEFAULT_DENSITY = 160; - - private final String mValue; - private final String mDisplayValue; - private final int mDensity; - private final int mSince; - - private Density(String value, String displayValue, int density, int since) { - mValue = value; - mDisplayValue = displayValue; - mDensity = density; - mSince = since; - } - - /** - * Returns the enum matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no match was found. - */ - public static Density getEnum(String value) { - for (Density orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - /** - * Returns the enum matching the given density value - * @param value The density value. - * @return the enum for the density value or null if no match was found. - */ - public static Density getEnum(int value) { - for (Density d : values()) { - if (d.mDensity == value) { - return d; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - public int getDpiValue() { - return mDensity; - } - - public int since() { - return mSince; - } - - public String getLegacyValue() { - if (this != NODPI) { - return String.format("%1$ddpi", getDpiValue()); - } - - return ""; - } - - @Override - public String getShortDisplayValue() { - return mDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mDisplayValue; - } - - public static int getIndex(Density value) { - int i = 0; - for (Density input : values()) { - if (value == input) { - return i; - } - - i++; - } - - return -1; - } - - public static Density getByIndex(int index) { - int i = 0; - for (Density value : values()) { - if (i == index) { - return value; - } - i++; - } - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return this != NODPI; // nodpi is not a valid config for devices. - } -} diff --git a/layoutlib_api/src/main/java/com/android/resources/FolderTypeRelationship.java b/layoutlib_api/src/main/java/com/android/resources/FolderTypeRelationship.java deleted file mode 100644 index 61a6d85..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/FolderTypeRelationship.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2007 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.resources; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * This class gives access to the bidirectional relationship between {@link ResourceType} and - * {@link ResourceFolderType}. - */ -public final class FolderTypeRelationship { - - private final static Map<ResourceType, List<ResourceFolderType>> mTypeToFolderMap = - new HashMap<ResourceType, List<ResourceFolderType>>(); - - private final static Map<ResourceFolderType, List<ResourceType>> mFolderToTypeMap = - new HashMap<ResourceFolderType, List<ResourceType>>(); - - static { - // generate the relationships in a temporary map - add(ResourceType.ANIM, ResourceFolderType.ANIM); - add(ResourceType.ANIMATOR, ResourceFolderType.ANIMATOR); - add(ResourceType.ARRAY, ResourceFolderType.VALUES); - add(ResourceType.ATTR, ResourceFolderType.VALUES); - add(ResourceType.BOOL, ResourceFolderType.VALUES); - add(ResourceType.COLOR, ResourceFolderType.VALUES); - add(ResourceType.COLOR, ResourceFolderType.COLOR); - add(ResourceType.DECLARE_STYLEABLE, ResourceFolderType.VALUES); - add(ResourceType.DIMEN, ResourceFolderType.VALUES); - add(ResourceType.DRAWABLE, ResourceFolderType.VALUES); - add(ResourceType.DRAWABLE, ResourceFolderType.DRAWABLE); - add(ResourceType.FRACTION, ResourceFolderType.VALUES); - add(ResourceType.ID, ResourceFolderType.VALUES); - add(ResourceType.INTEGER, ResourceFolderType.VALUES); - add(ResourceType.INTERPOLATOR, ResourceFolderType.INTERPOLATOR); - add(ResourceType.LAYOUT, ResourceFolderType.LAYOUT); - add(ResourceType.ID, ResourceFolderType.LAYOUT); - add(ResourceType.MENU, ResourceFolderType.MENU); - add(ResourceType.ID, ResourceFolderType.MENU); - add(ResourceType.MIPMAP, ResourceFolderType.MIPMAP); - add(ResourceType.PLURALS, ResourceFolderType.VALUES); - add(ResourceType.PUBLIC, ResourceFolderType.VALUES); - add(ResourceType.RAW, ResourceFolderType.RAW); - add(ResourceType.STRING, ResourceFolderType.VALUES); - add(ResourceType.STYLE, ResourceFolderType.VALUES); - add(ResourceType.STYLEABLE, ResourceFolderType.VALUES); - add(ResourceType.XML, ResourceFolderType.XML); - - makeSafe(); - } - - /** - * Returns a list of {@link ResourceType}s that can be generated from files inside a folder - * of the specified type. - * @param folderType The folder type. - * @return a list of {@link ResourceType}, possibly empty but never null. - */ - public static List<ResourceType> getRelatedResourceTypes(ResourceFolderType folderType) { - List<ResourceType> list = mFolderToTypeMap.get(folderType); - if (list != null) { - return list; - } - - return Collections.emptyList(); - } - - /** - * Returns a list of {@link ResourceFolderType} that can contain files generating resources - * of the specified type. - * @param resType the type of resource. - * @return a list of {@link ResourceFolderType}, possibly empty but never null. - */ - public static List<ResourceFolderType> getRelatedFolders(ResourceType resType) { - List<ResourceFolderType> list = mTypeToFolderMap.get(resType); - if (list != null) { - return list; - } - - return Collections.emptyList(); - } - - /** - * Returns true if the {@link ResourceType} and the {@link ResourceFolderType} values match. - * @param resType the resource type. - * @param folderType the folder type. - * @return true if files inside the folder of the specified {@link ResourceFolderType} - * could generate a resource of the specified {@link ResourceType} - */ - public static boolean match(ResourceType resType, ResourceFolderType folderType) { - List<ResourceFolderType> list = mTypeToFolderMap.get(resType); - - if (list != null) { - return list.contains(folderType); - } - - return false; - } - - /** - * Adds a {@link ResourceType} - {@link ResourceFolderType} relationship. this indicates that - * a file in the folder can generate a resource of the specified type. - * @param type The resourceType - * @param folder The {@link ResourceFolderType} - */ - private static void add(ResourceType type, ResourceFolderType folder) { - // first we add the folder to the list associated with the type. - List<ResourceFolderType> folderList = mTypeToFolderMap.get(type); - if (folderList == null) { - folderList = new ArrayList<ResourceFolderType>(); - mTypeToFolderMap.put(type, folderList); - } - if (folderList.indexOf(folder) == -1) { - folderList.add(folder); - } - - // now we add the type to the list associated with the folder. - List<ResourceType> typeList = mFolderToTypeMap.get(folder); - if (typeList == null) { - typeList = new ArrayList<ResourceType>(); - mFolderToTypeMap.put(folder, typeList); - } - if (typeList.indexOf(type) == -1) { - typeList.add(type); - } - } - - /** - * Makes the maps safe by replacing the current list values with unmodifiable lists. - */ - private static void makeSafe() { - for (ResourceType type : ResourceType.values()) { - List<ResourceFolderType> list = mTypeToFolderMap.get(type); - if (list != null) { - // replace with a unmodifiable list wrapper around the current list. - mTypeToFolderMap.put(type, Collections.unmodifiableList(list)); - } - } - - for (ResourceFolderType folder : ResourceFolderType.values()) { - List<ResourceType> list = mFolderToTypeMap.get(folder); - if (list != null) { - // replace with a unmodifiable list wrapper around the current list. - mFolderToTypeMap.put(folder, Collections.unmodifiableList(list)); - } - } - } -} diff --git a/layoutlib_api/src/main/java/com/android/resources/Keyboard.java b/layoutlib_api/src/main/java/com/android/resources/Keyboard.java deleted file mode 100644 index d6bc80a..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/Keyboard.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Keyboard enum. - * <p/>This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum Keyboard implements ResourceEnum { - NOKEY("nokeys", null, "No Keys", "No keyboard"), //$NON-NLS-1$ - QWERTY("qwerty", null, "Qwerty", "Qwerty keybard"), //$NON-NLS-1$ - TWELVEKEY("12key", "twelvekey", "12 Key", "12 key keyboard"); //$NON-NLS-1$ //$NON-NLS-2$ - - private final String mValue, mValue2; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private Keyboard(String value, String value2, String shortDisplayValue, - String longDisplayValue) { - mValue = value; - mValue2 = value2; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static Keyboard getEnum(String value) { - for (Keyboard kbrd : values()) { - if (kbrd.mValue.equals(value) || - (kbrd.mValue2 != null && kbrd.mValue2.equals(value))) { - return kbrd; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(Keyboard value) { - int i = 0; - for (Keyboard input : values()) { - if (value == input) { - return i; - } - - i++; - } - - return -1; - } - - public static Keyboard getByIndex(int index) { - int i = 0; - for (Keyboard value : values()) { - if (i == index) { - return value; - } - i++; - } - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } -} diff --git a/layoutlib_api/src/main/java/com/android/resources/KeyboardState.java b/layoutlib_api/src/main/java/com/android/resources/KeyboardState.java deleted file mode 100644 index 2eb7e00..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/KeyboardState.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Keyboard state enum. - * <p/>This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum KeyboardState implements ResourceEnum { - EXPOSED("keysexposed", "Exposed", "Exposed keyboard"), //$NON-NLS-1$ - HIDDEN("keyshidden", "Hidden", "Hidden keyboard"), //$NON-NLS-1$ - SOFT("keyssoft", "Soft", "Soft keyboard"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private KeyboardState(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static KeyboardState getEnum(String value) { - for (KeyboardState state : values()) { - if (state.mValue.equals(value)) { - return state; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(KeyboardState value) { - int i = 0; - for (KeyboardState input : values()) { - if (value == input) { - return i; - } - - i++; - } - - return -1; - } - - public static KeyboardState getByIndex(int index) { - int i = 0; - for (KeyboardState value : values()) { - if (i == index) { - return value; - } - i++; - } - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } - -} diff --git a/layoutlib_api/src/main/java/com/android/resources/LayoutDirection.java b/layoutlib_api/src/main/java/com/android/resources/LayoutDirection.java deleted file mode 100644 index fbc386d..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/LayoutDirection.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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.resources; - -/** - * Layout Direction enum. - */ -public enum LayoutDirection implements ResourceEnum { - LTR("ldltr", "LTR", "Left To Right"), //$NON-NLS-1$ - RTL("ldrtl", "RTL", "Right To Left"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private LayoutDirection(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static LayoutDirection getEnum(String value) { - for (LayoutDirection orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(LayoutDirection orientation) { - int i = 0; - for (LayoutDirection orient : values()) { - if (orient == orientation) { - return i; - } - - i++; - } - - return -1; - } - - public static LayoutDirection getByIndex(int index) { - int i = 0; - for (LayoutDirection orient : values()) { - if (i == index) { - return orient; - } - i++; - } - - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } -} diff --git a/layoutlib_api/src/main/java/com/android/resources/Navigation.java b/layoutlib_api/src/main/java/com/android/resources/Navigation.java deleted file mode 100644 index f857e5f..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/Navigation.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Navigation enum. - * <p/>This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum Navigation implements ResourceEnum { - NONAV("nonav", "None", "No navigation"), //$NON-NLS-1$ - DPAD("dpad", "D-pad", "D-pad navigation"), //$NON-NLS-1$ - TRACKBALL("trackball", "Trackball", "Trackball navigation"), //$NON-NLS-1$ - WHEEL("wheel", "Wheel", "Wheel navigation"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private Navigation(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static Navigation getEnum(String value) { - for (Navigation nav : values()) { - if (nav.mValue.equals(value)) { - return nav; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(Navigation value) { - int i = 0; - for (Navigation nav : values()) { - if (nav == value) { - return i; - } - - i++; - } - - return -1; - } - - public static Navigation getByIndex(int index) { - int i = 0; - for (Navigation value : values()) { - if (i == index) { - return value; - } - i++; - } - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } - -}
\ No newline at end of file diff --git a/layoutlib_api/src/main/java/com/android/resources/NavigationState.java b/layoutlib_api/src/main/java/com/android/resources/NavigationState.java deleted file mode 100644 index 63b8fea..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/NavigationState.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Navigation state enum. - * <p/>This is used in the resource folder names. - */ -public enum NavigationState implements ResourceEnum { - EXPOSED("navexposed", "Exposed", "Exposed navigation"), //$NON-NLS-1$ - HIDDEN("navhidden", "Hidden", "Hidden navigation"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private NavigationState(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static NavigationState getEnum(String value) { - for (NavigationState state : values()) { - if (state.mValue.equals(value)) { - return state; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(NavigationState value) { - int i = 0; - for (NavigationState input : values()) { - if (value == input) { - return i; - } - - i++; - } - - return -1; - } - - public static NavigationState getByIndex(int index) { - int i = 0; - for (NavigationState value : values()) { - if (i == index) { - return value; - } - i++; - } - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } - -} diff --git a/layoutlib_api/src/main/java/com/android/resources/NightMode.java b/layoutlib_api/src/main/java/com/android/resources/NightMode.java deleted file mode 100644 index 8fe1dd9..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/NightMode.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Night enum. - * <p/>This is used in the resource folder names. - */ -public enum NightMode implements ResourceEnum { - NOTNIGHT("notnight", "Not Night", "Day time"), - NIGHT("night", "Night", "Night time"); - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private NightMode(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static NightMode getEnum(String value) { - for (NightMode mode : values()) { - if (mode.mValue.equals(value)) { - return mode; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(NightMode value) { - int i = 0; - for (NightMode mode : values()) { - if (mode == value) { - return i; - } - - i++; - } - - return -1; - } - - public static NightMode getByIndex(int index) { - int i = 0; - for (NightMode value : values()) { - if (i == index) { - return value; - } - i++; - } - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } - -} diff --git a/layoutlib_api/src/main/java/com/android/resources/ResourceConstants.java b/layoutlib_api/src/main/java/com/android/resources/ResourceConstants.java deleted file mode 100644 index 748f88a..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/ResourceConstants.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.resources; - -/** - * Resource Constants. - */ -public class ResourceConstants { - - /** Default anim resource folder name, i.e. "anim" */ - public final static String FD_RES_ANIM = "anim"; //$NON-NLS-1$ - /** Default animator resource folder name, i.e. "animator" */ - public final static String FD_RES_ANIMATOR = "animator"; //$NON-NLS-1$ - /** Default color resource folder name, i.e. "color" */ - public final static String FD_RES_COLOR = "color"; //$NON-NLS-1$ - /** Default drawable resource folder name, i.e. "drawable" */ - public final static String FD_RES_DRAWABLE = "drawable"; //$NON-NLS-1$ - /** Default interpolator resource folder name, i.e. "interpolator" */ - public final static String FD_RES_INTERPOLATOR = "interpolator"; //$NON-NLS-1$ - /** Default layout resource folder name, i.e. "layout" */ - public final static String FD_RES_LAYOUT = "layout"; //$NON-NLS-1$ - /** Default menu resource folder name, i.e. "menu" */ - public final static String FD_RES_MENU = "menu"; //$NON-NLS-1$ - /** Default menu resource folder name, i.e. "mipmap" */ - public final static String FD_RES_MIPMAP = "mipmap"; //$NON-NLS-1$ - /** Default values resource folder name, i.e. "values" */ - public final static String FD_RES_VALUES = "values"; //$NON-NLS-1$ - /** Default xml resource folder name, i.e. "xml" */ - public final static String FD_RES_XML = "xml"; //$NON-NLS-1$ - /** Default raw resource folder name, i.e. "raw" */ - public final static String FD_RES_RAW = "raw"; //$NON-NLS-1$ - - /** Separator between the resource folder qualifier. */ - public final static String RES_QUALIFIER_SEP = "-"; //$NON-NLS-1$ - -} diff --git a/layoutlib_api/src/main/java/com/android/resources/ResourceEnum.java b/layoutlib_api/src/main/java/com/android/resources/ResourceEnum.java deleted file mode 100644 index 7f4e16a..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/ResourceEnum.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * An enum representing a resource qualifier value. - */ -public interface ResourceEnum { - - /** - * Returns the resource string. This is to be used in resource folder names. - */ - String getResourceValue(); - - /** - * Whether the value actually used on device. This returns true only if a device can report - * this value, false if it's just used to qualify resources. - */ - boolean isValidValueForDevice(); - - /** - * Whether the value is neither used for device nor resources. This returns false when - * the value is only used for internal usage in the custom editors. - */ - boolean isFakeValue(); - - /** - * Returns a short string for display value. The string does not need to show the context. - * <p/>For instance "exposed", which can be the value for the keyboard state or the navigation - * state, would be valid since something else in the UI is expected to show if this is about the - * keyboard or the navigation state. - * - * @see #getLongDisplayValue() - */ - String getShortDisplayValue(); - - /** - * Returns a long string for display value. This must not only include the enum value but - * context (qualifier) about what the value represents. - * <p/>For instance "Exposed keyboard", and "Export navigation", as "exposed" would not be - * enough to know what qualifier the value is about. - * - * @see #getShortDisplayValue() - */ - String getLongDisplayValue(); -} diff --git a/layoutlib_api/src/main/java/com/android/resources/ResourceFolderType.java b/layoutlib_api/src/main/java/com/android/resources/ResourceFolderType.java deleted file mode 100644 index 5a271cf..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/ResourceFolderType.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2007 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.resources; - -/** - * Enum representing a type of resource folder. - */ -public enum ResourceFolderType { - ANIM(ResourceConstants.FD_RES_ANIM), - ANIMATOR(ResourceConstants.FD_RES_ANIMATOR), - COLOR(ResourceConstants.FD_RES_COLOR), - DRAWABLE(ResourceConstants.FD_RES_DRAWABLE), - INTERPOLATOR(ResourceConstants.FD_RES_INTERPOLATOR), - LAYOUT(ResourceConstants.FD_RES_LAYOUT), - MENU(ResourceConstants.FD_RES_MENU), - MIPMAP(ResourceConstants.FD_RES_MIPMAP), - RAW(ResourceConstants.FD_RES_RAW), - VALUES(ResourceConstants.FD_RES_VALUES), - XML(ResourceConstants.FD_RES_XML); - - private final String mName; - - ResourceFolderType(String name) { - mName = name; - } - - /** - * Returns the folder name for this resource folder type. - */ - public String getName() { - return mName; - } - - /** - * Returns the enum by name. - * @param name The enum string value. - * @return the enum or null if not found. - */ - public static ResourceFolderType getTypeByName(String name) { - assert name.indexOf('-') == -1 : name; // use #getFolderType instead - for (ResourceFolderType rType : values()) { - if (rType.mName.equals(name)) { - return rType; - } - } - return null; - } - - /** - * Returns the {@link ResourceFolderType} from the folder name - * @param folderName The name of the folder. This must be a valid folder name in the format - * <code>resType[-resqualifiers[-resqualifiers[...]]</code> - * @return the <code>ResourceFolderType</code> representing the type of the folder, or - * <code>null</code> if no matching type was found. - */ - public static ResourceFolderType getFolderType(String folderName) { - int index = folderName.indexOf(ResourceConstants.RES_QUALIFIER_SEP); - if (index != -1) { - folderName = folderName.substring(0, index); - } - return getTypeByName(folderName); - } -} diff --git a/layoutlib_api/src/main/java/com/android/resources/ResourceType.java b/layoutlib_api/src/main/java/com/android/resources/ResourceType.java deleted file mode 100644 index e9d4d53..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/ResourceType.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2007 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.resources; - - -/** - * Enum representing a type of compiled resource. - */ -public enum ResourceType { - ANIM("anim", "Animation"), //$NON-NLS-1$ - ANIMATOR("animator", "Animator"), //$NON-NLS-1$ - ARRAY("array", "Array", "string-array", "integer-array"), //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-4$ - ATTR("attr", "Attr"), //$NON-NLS-1$ - BOOL("bool", "Boolean"), //$NON-NLS-1$ - COLOR("color", "Color"), //$NON-NLS-1$ - DECLARE_STYLEABLE("declare-styleable", "Declare Stylable"), //$NON-NLS-1$ - DIMEN("dimen", "Dimension"), //$NON-NLS-1$ - DRAWABLE("drawable", "Drawable"), //$NON-NLS-1$ - FRACTION("fraction", "Fraction"), //$NON-NLS-1$ - ID("id", "ID"), //$NON-NLS-1$ - INTEGER("integer", "Integer"), //$NON-NLS-1$ - INTERPOLATOR("interpolator", "Interpolator"), //$NON-NLS-1$ - LAYOUT("layout", "Layout"), //$NON-NLS-1$ - MENU("menu", "Menu"), //$NON-NLS-1$ - MIPMAP("mipmap", "Mip Map"), //$NON-NLS-1$ - PLURALS("plurals", "Plurals"), //$NON-NLS-1$ - RAW("raw", "Raw"), //$NON-NLS-1$ - STRING("string", "String"), //$NON-NLS-1$ - STYLE("style", "Style"), //$NON-NLS-1$ - STYLEABLE("styleable", "Styleable"), //$NON-NLS-1$ - XML("xml", "XML"), //$NON-NLS-1$ - // this is not actually used. Only there because they get parsed and since we want to - // detect new resource type, we need to have this one exist. - PUBLIC("public", "###"); //$NON-NLS-1$ //$NON-NLS-2$ - - private final String mName; - private final String mDisplayName; - private final String[] mAlternateXmlNames; - - ResourceType(String name, String displayName, String... alternateXmlNames) { - mName = name; - mDisplayName = displayName; - mAlternateXmlNames = alternateXmlNames; - } - - /** - * Returns the resource type name, as used by XML files. - */ - public String getName() { - return mName; - } - - /** - * Returns a translated display name for the resource type. - */ - public String getDisplayName() { - return mDisplayName; - } - - /** - * Returns the enum by its name as it appears in the XML or the R class. - * @param name name of the resource - * @return the matching {@link ResourceType} or <code>null</code> if no match was found. - */ - public static ResourceType getEnum(String name) { - for (ResourceType rType : values()) { - if (rType.mName.equals(name)) { - return rType; - } else if (rType.mAlternateXmlNames != null) { - // if there are alternate Xml Names, we test those too - for (String alternate : rType.mAlternateXmlNames) { - if (alternate.equals(name)) { - return rType; - } - } - } - } - return null; - } - - /** - * Returns an array with all the names defined by this enum. - */ - public static String[] getNames() { - ResourceType[] values = values(); - String[] names = new String[values.length]; - for (int i = values.length - 1; i >= 0; --i) { - names[i] = values[i].getName(); - } - return names; - } - - @Override - public String toString() { - return getName(); - } -} diff --git a/layoutlib_api/src/main/java/com/android/resources/ScreenOrientation.java b/layoutlib_api/src/main/java/com/android/resources/ScreenOrientation.java deleted file mode 100644 index b18753d..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/ScreenOrientation.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Screen Orientation enum. - * <p/>This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum ScreenOrientation implements ResourceEnum { - PORTRAIT("port", "Portrait", "Portrait Orientation"), //$NON-NLS-1$ - LANDSCAPE("land", "Landscape", "Landscape Orientation"), //$NON-NLS-1$ - SQUARE("square", "Square", "Square Orientation"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private ScreenOrientation(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static ScreenOrientation getEnum(String value) { - for (ScreenOrientation orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(ScreenOrientation orientation) { - int i = 0; - for (ScreenOrientation orient : values()) { - if (orient == orientation) { - return i; - } - - i++; - } - - return -1; - } - - public static ScreenOrientation getByIndex(int index) { - int i = 0; - for (ScreenOrientation orient : values()) { - if (i == index) { - return orient; - } - i++; - } - - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } - -} diff --git a/layoutlib_api/src/main/java/com/android/resources/ScreenRatio.java b/layoutlib_api/src/main/java/com/android/resources/ScreenRatio.java deleted file mode 100644 index bb575b0..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/ScreenRatio.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Screen Ratio enum. - * <p/>This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum ScreenRatio implements ResourceEnum { - NOTLONG("notlong", "Not Long", "Short screen aspect ratio"), //$NON-NLS-1$ - LONG("long", "Long", "Long screen aspect ratio"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private ScreenRatio(String value, String displayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = displayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static ScreenRatio getEnum(String value) { - for (ScreenRatio orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(ScreenRatio orientation) { - int i = 0; - for (ScreenRatio orient : values()) { - if (orient == orientation) { - return i; - } - - i++; - } - - return -1; - } - - public static ScreenRatio getByIndex(int index) { - int i = 0; - for (ScreenRatio orient : values()) { - if (i == index) { - return orient; - } - i++; - } - - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } - -} - diff --git a/layoutlib_api/src/main/java/com/android/resources/ScreenSize.java b/layoutlib_api/src/main/java/com/android/resources/ScreenSize.java deleted file mode 100644 index 4def540..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/ScreenSize.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Screen size enum. - * <p/>This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum ScreenSize implements ResourceEnum { - SMALL("small", "Small", "Small Screen"), //$NON-NLS-1$ - NORMAL("normal", "Normal", "Normal Screen"), //$NON-NLS-1$ - LARGE("large", "Large", "Large Screen"), //$NON-NLS-1$ - XLARGE("xlarge", "X-Large", "Extra Large Screen"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private ScreenSize(String value, String shortDisplayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = shortDisplayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static ScreenSize getEnum(String value) { - for (ScreenSize orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(ScreenSize orientation) { - int i = 0; - for (ScreenSize orient : values()) { - if (orient == orientation) { - return i; - } - - i++; - } - - return -1; - } - - public static ScreenSize getByIndex(int index) { - int i = 0; - for (ScreenSize orient : values()) { - if (i == index) { - return orient; - } - i++; - } - - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } - -} diff --git a/layoutlib_api/src/main/java/com/android/resources/TouchScreen.java b/layoutlib_api/src/main/java/com/android/resources/TouchScreen.java deleted file mode 100644 index 7eeeb08..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/TouchScreen.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * Touch screen enum. - * <p/>This is used in the manifest in the uses-configuration node and in the resource folder names. - */ -public enum TouchScreen implements ResourceEnum { - NOTOUCH("notouch", "No Touch", "No-touch screen"), //$NON-NLS-1$ - STYLUS("stylus", "Stylus", "Stylus-based touchscreen"), //$NON-NLS-1$ - FINGER("finger", "Finger", "Finger-based touchscreen"); //$NON-NLS-1$ - - private final String mValue; - private final String mShortDisplayValue; - private final String mLongDisplayValue; - - private TouchScreen(String value, String displayValue, String longDisplayValue) { - mValue = value; - mShortDisplayValue = displayValue; - mLongDisplayValue = longDisplayValue; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static TouchScreen getEnum(String value) { - for (TouchScreen orient : values()) { - if (orient.mValue.equals(value)) { - return orient; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mShortDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mLongDisplayValue; - } - - public static int getIndex(TouchScreen touch) { - int i = 0; - for (TouchScreen t : values()) { - if (t == touch) { - return i; - } - - i++; - } - - return -1; - } - - public static TouchScreen getByIndex(int index) { - int i = 0; - for (TouchScreen value : values()) { - if (i == index) { - return value; - } - i++; - } - - return null; - } - - @Override - public boolean isFakeValue() { - return false; - } - - @Override - public boolean isValidValueForDevice() { - return true; - } - -} diff --git a/layoutlib_api/src/main/java/com/android/resources/UiMode.java b/layoutlib_api/src/main/java/com/android/resources/UiMode.java deleted file mode 100644 index d1ddbc8..0000000 --- a/layoutlib_api/src/main/java/com/android/resources/UiMode.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -/** - * UI Mode enum. - * <p/>This is used in the resource folder names. - */ -public enum UiMode implements ResourceEnum { - NORMAL("", "Normal"), - CAR("car", "Car Dock"), - DESK("desk", "Desk Dock"), - TELEVISION("television", "Television"); - - private final String mValue; - private final String mDisplayValue; - - private UiMode(String value, String display) { - mValue = value; - mDisplayValue = display; - } - - /** - * Returns the enum for matching the provided qualifier value. - * @param value The qualifier value. - * @return the enum for the qualifier value or null if no matching was found. - */ - public static UiMode getEnum(String value) { - for (UiMode mode : values()) { - if (mode.mValue.equals(value)) { - return mode; - } - } - - return null; - } - - @Override - public String getResourceValue() { - return mValue; - } - - @Override - public String getShortDisplayValue() { - return mDisplayValue; - } - - @Override - public String getLongDisplayValue() { - return mDisplayValue; - } - - public static int getIndex(UiMode value) { - int i = 0; - for (UiMode mode : values()) { - if (mode == value) { - return i; - } - - i++; - } - - return -1; - } - - public static UiMode getByIndex(int index) { - int i = 0; - for (UiMode value : values()) { - if (i == index) { - return value; - } - i++; - } - return null; - } - - @Override - public boolean isFakeValue() { - return this == NORMAL; // NORMAL is not a real enum. it's used for internal state only. - } - - @Override - public boolean isValidValueForDevice() { - return this != NORMAL; - } -} diff --git a/layoutlib_api/src/main/java/com/android/util/Pair.java b/layoutlib_api/src/main/java/com/android/util/Pair.java deleted file mode 100644 index 7e797e0..0000000 --- a/layoutlib_api/src/main/java/com/android/util/Pair.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2010 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.util; - -/** - * A Pair class is simply a 2-tuple for use in this package. We might want to - * think about adding something like this to a more central utility place, or - * replace it by a common tuple class if one exists, or even rewrite the layout - * classes using this Pair by a more dedicated data structure (so we don't have - * to pass around generic signatures as is currently done, though at least the - * construction is helped a bit by the {@link #of} factory method. - * - * ================================================================================================= - * WARNING - * This copy of the class is to be used only by layoutlib and is not to be changed, EVER. - * To use Pair outside of layoutlib, use com.android.utils.Pair, found in common.jar instead. - * ================================================================================================= - * - * @param <S> The type of the first value - * @param <T> The type of the second value - * - * @deprecated This is used for backward compatibility with layoutlib_api. Use com.android.utils.Pair instead - */ -@Deprecated -public class Pair<S,T> { - private final S mFirst; - private final T mSecond; - - // Use {@link Pair#of} factory instead since it infers generic types - private Pair(S first, T second) { - this.mFirst = first; - this.mSecond = second; - } - - /** - * Return the first item in the pair - * - * @return the first item in the pair - */ - public S getFirst() { - return mFirst; - } - - /** - * Return the second item in the pair - * - * @return the second item in the pair - */ - public T getSecond() { - return mSecond; - } - - /** - * Constructs a new pair of the given two objects, inferring generic types. - * - * @param first the first item to store in the pair - * @param second the second item to store in the pair - * @param <S> the type of the first item - * @param <T> the type of the second item - * @return a new pair wrapping the two items - */ - public static <S,T> Pair<S,T> of(S first, T second) { - return new Pair<S,T>(first,second); - } - - @Override - public String toString() { - return "Pair [first=" + mFirst + ", second=" + mSecond + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode()); - result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode()); - return result; - } - - @SuppressWarnings("unchecked") - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Pair other = (Pair) obj; - if (mFirst == null) { - if (other.mFirst != null) - return false; - } else if (!mFirst.equals(other.mFirst)) - return false; - if (mSecond == null) { - if (other.mSecond != null) - return false; - } else if (!mSecond.equals(other.mSecond)) - return false; - return true; - } -} diff --git a/layoutlib_api/src/test/.classpath b/layoutlib_api/src/test/.classpath deleted file mode 100644 index 7564f2f..0000000 --- a/layoutlib_api/src/test/.classpath +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="java"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> - <classpathentry combineaccessrules="false" kind="src" path="/common"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-13.0.1.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/guava-tools/src.zip"/> - <classpathentry kind="output" path="bin"/> -</classpath> diff --git a/layoutlib_api/src/test/.project b/layoutlib_api/src/test/.project deleted file mode 100644 index 9f550a3..0000000 --- a/layoutlib_api/src/test/.project +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>common-tests</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> diff --git a/layoutlib_api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java b/layoutlib_api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java deleted file mode 100644 index 809eae7..0000000 --- a/layoutlib_api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -import com.android.resources.FolderTypeRelationship; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; - -import junit.framework.TestCase; - -public class FolderTypeRelationShipTest extends TestCase { - - public void testResourceType() { - // all resource type should be in the FolderTypeRelationShip map. - // loop on all the enum, and make sure there's at least one folder type for it. - for (ResourceType type : ResourceType.values()) { - assertTrue(type.getDisplayName(), - FolderTypeRelationship.getRelatedFolders(type).size() > 0); - } - } - - public void testResourceFolderType() { - // all resource folder type should generate at least one type of resource. - // loop on all the enum, and make sure there's at least one res type for it. - for (ResourceFolderType type : ResourceFolderType.values()) { - assertTrue(type.getName(), - FolderTypeRelationship.getRelatedResourceTypes(type).size() > 0); - } - } -} diff --git a/layoutlib_api/src/test/java/com/android/resources/ResourceFolderTypeTest.java b/layoutlib_api/src/test/java/com/android/resources/ResourceFolderTypeTest.java deleted file mode 100644 index a3c2c51..0000000 --- a/layoutlib_api/src/test/java/com/android/resources/ResourceFolderTypeTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2013 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.resources; - -import junit.framework.TestCase; - -public class ResourceFolderTypeTest extends TestCase { - public void test() { - assertEquals(ResourceFolderType.LAYOUT, ResourceFolderType.getFolderType("layout")); - assertEquals(ResourceFolderType.LAYOUT, ResourceFolderType.getFolderType("layout-port")); - assertEquals(ResourceFolderType.LAYOUT, ResourceFolderType.getFolderType("layout-port-sw600")); - assertEquals(ResourceFolderType.VALUES, ResourceFolderType.getFolderType("values")); - - assertNull(ResourceFolderType.getFolderType("")); - assertNull(ResourceFolderType.getFolderType("foo")); - } -} diff --git a/lint/cli/Android.mk b/lint/cli/Android.mk index dcd1792..00c736b 100644 --- a/lint/cli/Android.mk +++ b/lint/cli/Android.mk @@ -1,14 +1,26 @@ # Copyright 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. +# LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java) -LOCAL_JAVA_RESOURCE_DIRS := src/main/java - -LOCAL_JAR_MANIFEST := etc/manifest.txt +# The lint code has moved to tools/base/lint. +# The rule below uses the prebuilt lint.jar. +# +# If you want to run the tests, cd to tools/base/lint +# and run ./gradlew :lint:test -# If the dependency list is changed, etc/manifest.txt LOCAL_JAVA_LIBRARIES := \ common \ sdklib \ @@ -23,21 +35,8 @@ LOCAL_JAVA_LIBRARIES := \ LOCAL_MODULE := lint LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_JAVA_LIBRARY) - - -# Build all sub-directories -include $(call all-makefiles-under,$(LOCAL_PATH)) - -# Build tests -include $(CLEAR_VARS) - -# Only compile source java files in this lib. -LOCAL_SRC_FILES := $(call all-java-files-under, src/test/java) - -LOCAL_MODULE := lint_checks-tests -LOCAL_MODULE_TAGS := optional +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + ../../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) -LOCAL_JAVA_LIBRARIES := common sdklib lint_api lint_checks lint junit easymock asm-tools asm-tree-tools guava-tools layoutlib_api sdktestutils +include $(BUILD_HOST_PREBUILT) -include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java index 43d3727..947d27b 100644 --- a/lint/cli/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java @@ -874,4 +874,27 @@ public class ApiDetectorTest extends AbstractCheckTest { "apicheck/ApiSourceCheck2.class.data=>bin/classes/test/pkg/ApiSourceCheck2.class" )); } + + public void testInheritCompatLibrary() throws Exception { + assertEquals("" + + "src/test/pkg/MyActivityImpl.java:8: Error: Call requires API level 11 (current min is 1): android.app.Activity#isChangingConfigurations [NewApi]\n" + + " boolean isChanging = super.isChangingConfigurations();\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "src/test/pkg/MyActivityImpl.java:13: Error: Call requires API level 11 (current min is 1): android.app.Activity#isChangingConfigurations [NewApi]\n" + + " return super.isChangingConfigurations();\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "src/test/pkg/MyActivityImpl.java:12: Error: This method is not overriding anything with the current build target, but will in API level 11 (current target is 3): test.pkg.MyActivityImpl#isChangingConfigurations [Override]\n" + + " public boolean isChangingConfigurations() {\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "3 errors, 0 warnings\n", + + lintProject( + "apicheck/classpath=>.classpath", + "apicheck/minsdk1.xml=>AndroidManifest.xml", + "project.properties1=>project.properties", + "apicheck/MyActivityImpl.java.txt=>src/test/pkg/MyActivityImpl.java", + "apicheck/MyActivityImpl.class.data=>bin/classes/test/pkg/MyActivityImpl.class", + "apicheck/android-support-v4.jar.data=>libs/android-support-v4.jar" + )); + } } diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java new file mode 100644 index 0000000..f0ce60d --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2013 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.client.api.LintClient; +import com.android.tools.lint.detector.api.Detector; +import com.android.tools.lint.detector.api.Issue; +import com.android.tools.lint.detector.api.Project; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +@SuppressWarnings("javadoc") +public class ManifestTypoDetectorTest extends AbstractCheckTest { + @Override + protected Detector getDetector() { + return new ManifestTypoDetector(); + } + + private Set<Issue> mEnabled = new HashSet<Issue>(); + + @Override + protected TestConfiguration getConfiguration(LintClient client, Project project) { + return new TestConfiguration(client, project, null) { + @Override + public boolean isEnabled(@NonNull Issue issue) { + return super.isEnabled(issue) && mEnabled.contains(issue); + } + }; + } + + public void testOk() throws Exception { + mEnabled = Collections.singleton(ManifestTypoDetector.ISSUE); + assertEquals( + "No warnings.", + lintProject( + "typo_not_found.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testTypoUsesSdk() throws Exception { + mEnabled = Collections.singleton(ManifestTypoDetector.ISSUE); + assertEquals( + "AndroidManifest.xml:7: " + + "Warning: <use-sdk> looks like a typo; did you mean <uses-sdk> ? [ManifestTypos]\n" + + " <use-sdk android:minSdkVersion=\"14\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings\n" + + "", + + lintProject( + "typo_uses_sdk.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testTypoUsesSdk2() throws Exception { + mEnabled = Collections.singleton(ManifestTypoDetector.ISSUE); + assertEquals( + "AndroidManifest.xml:7: " + + "Warning: <user-sdk> looks like a typo; did you mean <uses-sdk> ? [ManifestTypos]\n" + + " <user-sdk android:minSdkVersion=\"14\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings\n" + + "", + + lintProject( + "typo_uses_sdk2.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testTypoUsesPermission() throws Exception { + mEnabled = Collections.singleton(ManifestTypoDetector.ISSUE); + assertEquals( + "AndroidManifest.xml:9: " + + "Warning: <use-permission> looks like a typo; " + + "did you mean <uses-permission> ? [ManifestTypos]\n" + + " <use-permission android:name=\"com.example.helloworld.permission\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings\n" + + "", + + lintProject( + "typo_uses_permission.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testTypoUsesPermission2() throws Exception { + mEnabled = Collections.singleton(ManifestTypoDetector.ISSUE); + assertEquals( + "AndroidManifest.xml:9: " + + "Warning: <user-permission> looks like a typo; " + + "did you mean <uses-permission> ? [ManifestTypos]\n" + + " <user-permission android:name=\"com.example.helloworld.permission\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings\n" + + "", + + lintProject( + "typo_uses_permission2.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testTypoUsesFeature() throws Exception { + mEnabled = Collections.singleton(ManifestTypoDetector.ISSUE); + assertEquals( + "AndroidManifest.xml:11: " + + "Warning: <use-feature> looks like a typo; " + + "did you mean <uses-feature> ? [ManifestTypos]\n" + + " <use-feature android:name=\"android.hardware.wifi\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings\n" + + "", + + lintProject( + "typo_uses_feature.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testTypoUsesFeature2() throws Exception { + mEnabled = Collections.singleton(ManifestTypoDetector.ISSUE); + assertEquals( + "AndroidManifest.xml:11: " + + "Warning: <user-feature> looks like a typo; " + + "did you mean <uses-feature> ? [ManifestTypos]\n" + + " <user-feature android:name=\"android.hardware.wifi\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings\n" + + "", + + lintProject( + "typo_uses_feature2.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testTypoUsesLibrary() throws Exception { + mEnabled = Collections.singleton(ManifestTypoDetector.ISSUE); + assertEquals( + "AndroidManifest.xml:16: " + + "Warning: <use-library> looks like a typo; " + + "did you mean <uses-library> ? [ManifestTypos]\n" + + " <use-library android:name=\"com.example.helloworld\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings\n" + + "", + + lintProject( + "typo_uses_library.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testTypoUsesLibrary2() throws Exception { + mEnabled = Collections.singleton(ManifestTypoDetector.ISSUE); + assertEquals( + "AndroidManifest.xml:16: " + + "Warning: <user-library> looks like a typo; " + + "did you mean <uses-library> ? [ManifestTypos]\n" + + " <user-library android:name=\"com.example.helloworld\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings\n" + + "", + + lintProject( + "typo_uses_library2.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } +} diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data b/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data Binary files differnew file mode 100644 index 0000000..6ae6b28 --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt new file mode 100644 index 0000000..e39daf8 --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt @@ -0,0 +1,15 @@ +package test.pkg; + +import android.app.Activity; +import android.support.v4.app.FragmentActivity; + +public class MyActivityImpl extends FragmentActivity { + public void test() { + boolean isChanging = super.isChangingConfigurations(); + } + + @Override + public boolean isChangingConfigurations() { + return super.isChangingConfigurations(); + } +} diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data b/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data Binary files differnew file mode 100644 index 0000000..6080877 --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml new file mode 100644 index 0000000..9cd3640 --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <uses-permission android:name="com.example.helloworld.permission" /> + + <uses-feature android:name="android.hardware.wifi" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <uses-library android:name="com.example.helloworld" /> + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml new file mode 100644 index 0000000..cff7f2e --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <uses-permission android:name="com.example.helloworld.permission" /> + + <use-feature android:name="android.hardware.wifi" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <uses-library android:name="com.example.helloworld" /> + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml new file mode 100644 index 0000000..d4d8f6e --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <uses-permission android:name="com.example.helloworld.permission" /> + + <user-feature android:name="android.hardware.wifi" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <uses-library android:name="com.example.helloworld" /> + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml new file mode 100644 index 0000000..5273642 --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <uses-permission android:name="com.example.helloworld.permission" /> + + <uses-feature android:name="android.hardware.wifi" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <use-library android:name="com.example.helloworld" /> + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml new file mode 100644 index 0000000..966caf4 --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <uses-permission android:name="com.example.helloworld.permission" /> + + <uses-feature android:name="android.hardware.wifi" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <user-library android:name="com.example.helloworld" /> + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml new file mode 100644 index 0000000..18a31ae --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <use-permission android:name="com.example.helloworld.permission" /> + + <uses-feature android:name="android.hardware.wifi" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <uses-library android:name="com.example.helloworld" /> + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml new file mode 100644 index 0000000..2f070c5 --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <user-permission android:name="com.example.helloworld.permission" /> + + <uses-feature android:name="android.hardware.wifi" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <uses-library android:name="com.example.helloworld" /> + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml new file mode 100644 index 0000000..c086515 --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <use-sdk android:minSdkVersion="14" /> + + <uses-permission android:name="com.example.helloworld.permission" /> + + <uses-feature android:name="android.hardware.wifi" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <uses-library android:name="com.example.helloworld" /> + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml new file mode 100644 index 0000000..148a3fc --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <user-sdk android:minSdkVersion="14" /> + + <uses-permission android:name="com.example.helloworld.permission" /> + + <uses-feature android:name="android.hardware.wifi" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <uses-library android:name="com.example.helloworld" /> + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/lint/libs/lint_api/Android.mk b/lint/libs/lint_api/Android.mk index 1d70a25..3510559 100644 --- a/lint/libs/lint_api/Android.mk +++ b/lint/libs/lint_api/Android.mk @@ -13,12 +13,14 @@ # limitations under the License. LOCAL_PATH := $(call my-dir) - include $(CLEAR_VARS) -# Only compile source java files in this lib. -LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java) -LOCAL_JAVA_RESOURCE_DIRS := src/main/java +# The lint_api code has moved to tools/base/lint_api. +# The rule below uses the prebuilt lint_api.jar. +# +# If you want to run the tests, cd to tools/base/lint_api +# and run ./gradlew :lint_api:test + LOCAL_JAVA_LIBRARIES := \ lombok-ast-0.2 \ common \ @@ -31,7 +33,8 @@ LOCAL_JAVA_LIBRARIES := \ LOCAL_MODULE := lint_api LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_JAVA_LIBRARY) +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + ../../../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) + +include $(BUILD_HOST_PREBUILT) -# Build all sub-directories -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/AsmVisitor.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/AsmVisitor.java deleted file mode 100644 index e8a4bb5..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/AsmVisitor.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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.client.api; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -import com.google.common.annotations.Beta; - -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Specialized visitor for running detectors on a class object model. - * <p> - * It operates in two phases: - * <ol> - * <li> First, it computes a set of maps where it generates a map from each - * significant method name to a list of detectors to consult for that method - * name. The set of method names that a detector is interested in is provided - * by the detectors themselves. - * <li> Second, it iterates over the DOM a single time. For each method call it finds, - * it dispatches to any check that has registered interest in that method name. - * <li> Finally, it runs a full check on those class scanners that do not register - * specific method names to be checked. This is intended for those detectors - * that do custom work, not related specifically to method calls. - * </ol> - * It also notifies all the detectors before and after the document is processed - * such that they can do pre- and post-processing. - * <p> - * <b>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.</b> - */ -@Beta -class AsmVisitor { - /** - * Number of distinct node types specified in {@link AbstractInsnNode}. Sadly - * there isn't a max-constant there, so update this along with ASM library - * updates. - */ - private static final int TYPE_COUNT = AbstractInsnNode.LINE + 1; - private final Map<String, List<ClassScanner>> mMethodNameToChecks = - new HashMap<String, List<ClassScanner>>(); - private final Map<String, List<ClassScanner>> mMethodOwnerToChecks = - new HashMap<String, List<ClassScanner>>(); - private final List<Detector> mFullClassChecks = new ArrayList<Detector>(); - - private final List<? extends Detector> mAllDetectors; - private List<ClassScanner>[] mNodeTypeDetectors; - - // Really want this: - //<T extends List<Detector> & Detector.ClassScanner> ClassVisitor(T xmlDetectors) { - // but it makes client code tricky and ugly. - @SuppressWarnings("unchecked") - AsmVisitor(@NonNull LintClient client, @NonNull List<? extends Detector> classDetectors) { - mAllDetectors = classDetectors; - - // TODO: Check appliesTo() for files, and find a quick way to enable/disable - // rules when running through a full project! - for (Detector detector : classDetectors) { - Detector.ClassScanner scanner = (Detector.ClassScanner) detector; - - boolean checkFullClass = true; - - Collection<String> names = scanner.getApplicableCallNames(); - if (names != null) { - checkFullClass = false; - for (String element : names) { - List<Detector.ClassScanner> list = mMethodNameToChecks.get(element); - if (list == null) { - list = new ArrayList<Detector.ClassScanner>(); - mMethodNameToChecks.put(element, list); - } - list.add(scanner); - } - } - - Collection<String> owners = scanner.getApplicableCallOwners(); - if (owners != null) { - checkFullClass = false; - for (String element : owners) { - List<Detector.ClassScanner> list = mMethodOwnerToChecks.get(element); - if (list == null) { - list = new ArrayList<Detector.ClassScanner>(); - mMethodOwnerToChecks.put(element, list); - } - list.add(scanner); - } - } - - int[] types = scanner.getApplicableAsmNodeTypes(); - if (types != null) { - checkFullClass = false; - for (int type : types) { - if (type < 0 || type >= TYPE_COUNT) { - // Can't support this node type: looks like ASM wasn't updated correctly. - client.log(null, "Out of range node type %1$d from detector %2$s", - type, scanner); - continue; - } - if (mNodeTypeDetectors == null) { - mNodeTypeDetectors = new List[TYPE_COUNT]; - } - List<ClassScanner> checks = mNodeTypeDetectors[type]; - if (checks == null) { - checks = new ArrayList<ClassScanner>(); - mNodeTypeDetectors[type] = checks; - } - checks.add(scanner); - } - } - - if (checkFullClass) { - mFullClassChecks.add(detector); - } - } - } - - @SuppressWarnings("rawtypes") // ASM API uses raw types - void runClassDetectors(ClassContext context) { - ClassNode classNode = context.getClassNode(); - - for (Detector detector : mAllDetectors) { - detector.beforeCheckFile(context); - } - - for (Detector detector : mFullClassChecks) { - Detector.ClassScanner scanner = (Detector.ClassScanner) detector; - scanner.checkClass(context, classNode); - detector.afterCheckFile(context); - } - - if (!mMethodNameToChecks.isEmpty() || !mMethodOwnerToChecks.isEmpty() || - mNodeTypeDetectors != null && mNodeTypeDetectors.length > 0) { - List methodList = classNode.methods; - for (Object m : methodList) { - MethodNode method = (MethodNode) m; - InsnList nodes = method.instructions; - for (int i = 0, n = nodes.size(); i < n; i++) { - AbstractInsnNode instruction = nodes.get(i); - int type = instruction.getType(); - if (type == AbstractInsnNode.METHOD_INSN) { - MethodInsnNode call = (MethodInsnNode) instruction; - - String owner = call.owner; - List<ClassScanner> scanners = mMethodOwnerToChecks.get(owner); - if (scanners != null) { - for (ClassScanner scanner : scanners) { - scanner.checkCall(context, classNode, method, call); - } - } - - String name = call.name; - scanners = mMethodNameToChecks.get(name); - if (scanners != null) { - for (ClassScanner scanner : scanners) { - scanner.checkCall(context, classNode, method, call); - } - } - } - - if (mNodeTypeDetectors != null && type < mNodeTypeDetectors.length) { - List<ClassScanner> scanners = mNodeTypeDetectors[type]; - if (scanners != null) { - for (ClassScanner scanner : scanners) { - scanner.checkInstruction(context, classNode, method, instruction); - } - } - } - } - } - } - - for (Detector detector : mAllDetectors) { - detector.afterCheckFile(context); - } - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/CircularDependencyException.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/CircularDependencyException.java deleted file mode 100644 index 337eb27..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/CircularDependencyException.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.client.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Project; -import com.google.common.annotations.Beta; - -/** - * Exception thrown when there is a circular dependency, such as a circular dependency - * of library mProject references - * <p> - * <b>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.</b> - */ -@Beta -public class CircularDependencyException extends RuntimeException { - @Nullable - private Project mProject; - - @Nullable - private Location mLocation; - - CircularDependencyException(@NonNull String message) { - super(message); - } - - /** - * Returns the associated project, if any - * - * @return the associated project, if any - */ - @Nullable - public Project getProject() { - return mProject; - } - - /** - * Sets the associated project, if any - * - * @param project the associated project, if any - */ - public void setProject(@Nullable Project project) { - mProject = project; - } - - /** - * Returns the associated location, if any - * - * @return the associated location, if any - */ - @Nullable - public Location getLocation() { - return mLocation; - } - - /** - * Sets the associated location, if any - * - * @param location the associated location, if any - */ - public void setLocation(@Nullable Location location) { - mLocation = location; - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/Configuration.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/Configuration.java deleted file mode 100644 index d233be7..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/Configuration.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Severity; -import com.google.common.annotations.Beta; - -/** - * Lint configuration for an Android project such as which specific rules to include, - * which specific rules to exclude, and which specific errors to ignore. - * <p/> - * <b>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.</b> - */ -@Beta -public abstract class Configuration { - /** - * Checks whether this issue should be ignored because the user has already - * suppressed the error? Note that this refers to individual issues being - * suppressed/ignored, not a whole detector being disabled via something - * like {@link #isEnabled(Issue)}. - * - * @param context the context used by the detector when the issue was found - * @param issue the issue that was found - * @param location the location of the issue - * @param message the associated user message - * @param data additional information about an issue (see - * {@link LintClient#report(Context, Issue, Severity, Location, String, Object)} - * for more information - * @return true if this issue should be suppressed - */ - public boolean isIgnored( - @NonNull Context context, - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - return false; - } - - /** - * Returns false if the given issue has been disabled. This is just - * a convenience method for {@code getSeverity(issue) != Severity.IGNORE}. - * - * @param issue the issue to check - * @return false if the issue has been disabled - */ - public boolean isEnabled(@NonNull Issue issue) { - return getSeverity(issue) != Severity.IGNORE; - } - - /** - * Returns the severity for a given issue. This is the same as the - * {@link Issue#getDefaultSeverity()} unless the user has selected a custom - * severity (which is tool context dependent). - * - * @param issue the issue to look up the severity from - * @return the severity use for issues for the given detector - */ - public Severity getSeverity(@NonNull Issue issue) { - return issue.getDefaultSeverity(); - } - - // Editing configurations - - /** - * Marks the given warning as "ignored". - * - * @param context The scanning context - * @param issue the issue to be ignored - * @param location The location to ignore the warning at, if any - * @param message The message for the warning - * @param data The corresponding data, or null - */ - public abstract void ignore( - @NonNull Context context, - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data); - - /** - * Sets the severity to be used for this issue. - * - * @param issue the issue to set the severity for - * @param severity the severity to associate with this issue, or null to - * reset the severity to the default - */ - public abstract void setSeverity(@NonNull Issue issue, @Nullable Severity severity); - - // Bulk editing support - - /** - * Marks the beginning of a "bulk" editing operation with repeated calls to - * {@link #setSeverity} or {@link #ignore}. After all the values have been - * set, the client <b>must</b> call {@link #finishBulkEditing()}. This - * allows configurations to avoid doing expensive I/O (such as writing out a - * config XML file) for each and every editing operation when they are - * applied in bulk, such as from a configuration dialog's "Apply" action. - */ - public void startBulkEditing() { - } - - /** - * Marks the end of a "bulk" editing operation, where values should be - * committed to persistent storage. See {@link #startBulkEditing()} for - * details. - */ - public void finishBulkEditing() { - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java deleted file mode 100644 index db82556..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Project; -import com.android.tools.lint.detector.api.Severity; -import com.google.common.annotations.Beta; -import com.google.common.io.Closeables; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXParseException; - -import java.io.BufferedInputStream; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -/** - * Default implementation of a {@link Configuration} which reads and writes - * configuration data into {@code lint.xml} in the project directory. - * <p/> - * <b>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.</b> - */ -@Beta -public class DefaultConfiguration extends Configuration { - private final LintClient mClient; - private static final String CONFIG_FILE_NAME = "lint.xml"; //$NON-NLS-1$ - - // Lint XML File - @NonNull - private static final String TAG_ISSUE = "issue"; //$NON-NLS-1$ - @NonNull - private static final String ATTR_ID = "id"; //$NON-NLS-1$ - @NonNull - private static final String ATTR_SEVERITY = "severity"; //$NON-NLS-1$ - @NonNull - private static final String ATTR_PATH = "path"; //$NON-NLS-1$ - @NonNull - private static final String TAG_IGNORE = "ignore"; //$NON-NLS-1$ - - private final Configuration mParent; - private final Project mProject; - private final File mConfigFile; - private boolean mBulkEditing; - - /** Map from id to list of project-relative paths for suppressed warnings */ - private Map<String, List<String>> mSuppressed; - - /** - * Map from id to custom {@link Severity} override - */ - private Map<String, Severity> mSeverity; - - protected DefaultConfiguration( - @NonNull LintClient client, - @Nullable Project project, - @Nullable Configuration parent, - @NonNull File configFile) { - mClient = client; - mProject = project; - mParent = parent; - mConfigFile = configFile; - } - - protected DefaultConfiguration( - @NonNull LintClient client, - @NonNull Project project, - @Nullable Configuration parent) { - this(client, project, parent, new File(project.getDir(), CONFIG_FILE_NAME)); - } - - /** - * Creates a new {@link DefaultConfiguration} - * - * @param client the client to report errors to etc - * @param project the associated project - * @param parent the parent/fallback configuration or null - * @return a new configuration - */ - @NonNull - public static DefaultConfiguration create( - @NonNull LintClient client, - @NonNull Project project, - @Nullable Configuration parent) { - return new DefaultConfiguration(client, project, parent); - } - - /** - * Creates a new {@link DefaultConfiguration} for the given lint config - * file, not affiliated with a project. This is used for global - * configurations. - * - * @param client the client to report errors to etc - * @param lintFile the lint file containing the configuration - * @return a new configuration - */ - @NonNull - public static DefaultConfiguration create(@NonNull LintClient client, @NonNull File lintFile) { - return new DefaultConfiguration(client, null /*project*/, null /*parent*/, lintFile); - } - - @Override - public boolean isIgnored( - @NonNull Context context, - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - ensureInitialized(); - - String id = issue.getId(); - List<String> paths = mSuppressed.get(id); - if (paths != null && location != null) { - File file = location.getFile(); - String relativePath = context.getProject().getRelativePath(file); - for (String suppressedPath : paths) { - if (suppressedPath.equals(relativePath)) { - return true; - } - } - } - - if (mParent != null) { - return mParent.isIgnored(context, issue, location, message, data); - } - - return false; - } - - @NonNull - protected Severity getDefaultSeverity(@NonNull Issue issue) { - if (!issue.isEnabledByDefault()) { - return Severity.IGNORE; - } - - return issue.getDefaultSeverity(); - } - - @Override - @NonNull - public Severity getSeverity(@NonNull Issue issue) { - ensureInitialized(); - - Severity severity = mSeverity.get(issue.getId()); - if (severity != null) { - return severity; - } - - if (mParent != null) { - return mParent.getSeverity(issue); - } - - return getDefaultSeverity(issue); - } - - private void ensureInitialized() { - if (mSuppressed == null) { - readConfig(); - } - } - - private void formatError(String message, Object... args) { - if (args != null && args.length > 0) { - message = String.format(message, args); - } - message = "Failed to parse lint.xml configuration file: " + message; - LintDriver driver = new LintDriver(new IssueRegistry() { - @Override @NonNull public List<Issue> getIssues() { - return Collections.emptyList(); - } - }, mClient); - mClient.report(new Context(driver, mProject, mProject, mConfigFile), - IssueRegistry.LINT_ERROR, - mProject.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR), - Location.create(mConfigFile), message, null); - } - - private void readConfig() { - mSuppressed = new HashMap<String, List<String>>(); - mSeverity = new HashMap<String, Severity>(); - - if (!mConfigFile.exists()) { - return; - } - - @SuppressWarnings("resource") // Eclipse doesn't know about Closeables.closeQuietly - BufferedInputStream input = null; - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - input = new BufferedInputStream(new FileInputStream(mConfigFile)); - InputSource source = new InputSource(input); - factory.setNamespaceAware(false); - factory.setValidating(false); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document document = builder.parse(source); - NodeList issues = document.getElementsByTagName(TAG_ISSUE); - for (int i = 0, count = issues.getLength(); i < count; i++) { - Node node = issues.item(i); - Element element = (Element) node; - String id = element.getAttribute(ATTR_ID); - if (id.isEmpty()) { - formatError("Invalid lint config file: Missing required issue id attribute"); - continue; - } - - NamedNodeMap attributes = node.getAttributes(); - for (int j = 0, n = attributes.getLength(); j < n; j++) { - Node attribute = attributes.item(j); - String name = attribute.getNodeName(); - String value = attribute.getNodeValue(); - if (ATTR_ID.equals(name)) { - // already handled - } else if (ATTR_SEVERITY.equals(name)) { - for (Severity severity : Severity.values()) { - if (value.equalsIgnoreCase(severity.name())) { - mSeverity.put(id, severity); - break; - } - } - } else { - formatError("Unexpected attribute \"%1$s\"", name); - } - } - - // Look up ignored errors - NodeList childNodes = element.getChildNodes(); - if (childNodes.getLength() > 0) { - for (int j = 0, n = childNodes.getLength(); j < n; j++) { - Node child = childNodes.item(j); - if (child.getNodeType() == Node.ELEMENT_NODE) { - Element ignore = (Element) child; - String path = ignore.getAttribute(ATTR_PATH); - if (path.isEmpty()) { - formatError("Missing required %1$s attribute under %2$s", - ATTR_PATH, id); - } else { - List<String> paths = mSuppressed.get(id); - if (paths == null) { - paths = new ArrayList<String>(n / 2 + 1); - mSuppressed.put(id, paths); - } - paths.add(path); - } - } - } - } - } - } catch (SAXParseException e) { - formatError(e.getMessage()); - } catch (Exception e) { - mClient.log(e, null); - } finally { - Closeables.closeQuietly(input); - } - } - - private void writeConfig() { - try { - // Write the contents to a new file first such that we don't clobber the - // existing file if some I/O error occurs. - File file = new File(mConfigFile.getParentFile(), - mConfigFile.getName() + ".new"); //$NON-NLS-1$ - - Writer writer = new BufferedWriter(new FileWriter(file)); - writer.write( - "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + //$NON-NLS-1$ - "<lint>\n"); //$NON-NLS-1$ - - if (!mSuppressed.isEmpty() || !mSeverity.isEmpty()) { - // Process the maps in a stable sorted order such that if the - // files are checked into version control with the project, - // there are no random diffs just because hashing algorithms - // differ: - Set<String> idSet = new HashSet<String>(); - for (String id : mSuppressed.keySet()) { - idSet.add(id); - } - for (String id : mSeverity.keySet()) { - idSet.add(id); - } - List<String> ids = new ArrayList<String>(idSet); - Collections.sort(ids); - - for (String id : ids) { - writer.write(" <"); //$NON-NLS-1$ - writer.write(TAG_ISSUE); - writeAttribute(writer, ATTR_ID, id); - Severity severity = mSeverity.get(id); - if (severity != null) { - writeAttribute(writer, ATTR_SEVERITY, - severity.name().toLowerCase(Locale.US)); - } - - List<String> paths = mSuppressed.get(id); - if (paths != null && !paths.isEmpty()) { - writer.write('>'); - writer.write('\n'); - // The paths are already kept in sorted order when they are modified - // by ignore(...) - for (String path : paths) { - writer.write(" <"); //$NON-NLS-1$ - writer.write(TAG_IGNORE); - writeAttribute(writer, ATTR_PATH, path); - writer.write(" />\n"); //$NON-NLS-1$ - } - writer.write(" </"); //$NON-NLS-1$ - writer.write(TAG_ISSUE); - writer.write('>'); - writer.write('\n'); - } else { - writer.write(" />\n"); //$NON-NLS-1$ - } - } - } - - writer.write("</lint>"); //$NON-NLS-1$ - writer.close(); - - // Move file into place: move current version to lint.xml~ (removing the old ~ file - // if it exists), then move the new version to lint.xml. - File oldFile = new File(mConfigFile.getParentFile(), - mConfigFile.getName() + '~'); //$NON-NLS-1$ - if (oldFile.exists()) { - oldFile.delete(); - } - if (mConfigFile.exists()) { - mConfigFile.renameTo(oldFile); - } - boolean ok = file.renameTo(mConfigFile); - if (ok && oldFile.exists()) { - oldFile.delete(); - } - } catch (Exception e) { - mClient.log(e, null); - } - } - - private static void writeAttribute( - @NonNull Writer writer, @NonNull String name, @NonNull String value) - throws IOException { - writer.write(' '); - writer.write(name); - writer.write('='); - writer.write('"'); - writer.write(value); - writer.write('"'); - } - - @Override - public void ignore( - @NonNull Context context, - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - // This configuration only supports suppressing warnings on a per-file basis - if (location != null) { - ignore(issue, location.getFile()); - } - } - - /** - * Marks the given issue and file combination as being ignored. - * - * @param issue the issue to be ignored in the given file - * @param file the file to ignore the issue in - */ - public void ignore(@NonNull Issue issue, @NonNull File file) { - ensureInitialized(); - - String path = mProject != null ? mProject.getRelativePath(file) : file.getPath(); - - List<String> paths = mSuppressed.get(issue.getId()); - if (paths == null) { - paths = new ArrayList<String>(); - mSuppressed.put(issue.getId(), paths); - } - paths.add(path); - - // Keep paths sorted alphabetically; makes XML output stable - Collections.sort(paths); - - if (!mBulkEditing) { - writeConfig(); - } - } - - @Override - public void setSeverity(@NonNull Issue issue, @Nullable Severity severity) { - ensureInitialized(); - - String id = issue.getId(); - if (severity == null) { - mSeverity.remove(id); - } else { - mSeverity.put(id, severity); - } - - if (!mBulkEditing) { - writeConfig(); - } - } - - @Override - public void startBulkEditing() { - mBulkEditing = true; - } - - @Override - public void finishBulkEditing() { - mBulkEditing = false; - writeConfig(); - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/DefaultSdkInfo.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/DefaultSdkInfo.java deleted file mode 100644 index 2f54659..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/DefaultSdkInfo.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * 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} - * <p> - * <b>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.</b> - */ -@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<String,String> PARENTS = Maps.newHashMapWithExpectedSize(CLASS_COUNT); - private static final Set<String> 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<String, String> INTERFACES = new HashMap<String, String>(2); - static { - INTERFACES.put(CHECKED_TEXT_VIEW, CHECKABLE); - INTERFACES.put(COMPOUND_BUTTON, CHECKABLE); - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/IDomParser.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/IDomParser.java deleted file mode 100644 index 1a70fac..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/IDomParser.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.XmlContext; -import com.google.common.annotations.Beta; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -/** - * A wrapper for an XML parser. This allows tools integrating lint to map directly - * to builtin services, such as already-parsed data structures in XML editors. - * <p/> - * <b>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.</b> - */ -@Beta -public interface IDomParser { - /** - * Parse the file pointed to by the given context and return as a Document - * - * @param context the context pointing to the file to be parsed, typically - * via {@link Context#getContents()} but the file handle ( - * {@link Context#file} can also be used to map to an existing - * editor buffer in the surrounding tool, etc) - * @return the parsed DOM document, or null if parsing fails - */ - @Nullable - Document parseXml(@NonNull XmlContext context); - - /** - * Returns a {@link Location} for the given DOM node - * - * @param context information about the file being parsed - * @param node the node to create a location for - * @return a location for the given node - */ - @NonNull - Location getLocation(@NonNull XmlContext context, @NonNull Node node); - - /** - * Returns a {@link Location} for the given DOM node. Like - * {@link #getLocation(XmlContext, Node)}, but allows a position range that - * is a subset of the node range. - * - * @param context information about the file being parsed - * @param node the node to create a location for - * @param start the starting position within the node, inclusive - * @param end the ending position within the node, exclusive - * @return a location for the given node - */ - @NonNull - Location getLocation(@NonNull XmlContext context, @NonNull Node node, int start, int end); - - /** - * Creates a light-weight handle to a location for the given node. It can be - * turned into a full fledged location by - * {@link com.android.tools.lint.detector.api.Location.Handle#resolve()}. - * - * @param context the context providing the node - * @param node the node (element or attribute) to create a location handle - * for - * @return a location handle - */ - @NonNull - Location.Handle createLocationHandle(@NonNull XmlContext context, @NonNull Node node); - - /** - * Dispose any data structures held for the given context. - * @param context information about the file previously parsed - * @param document the document that was parsed and is now being disposed - */ - void dispose(@NonNull XmlContext context, @NonNull Document document); -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/IJavaParser.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/IJavaParser.java deleted file mode 100644 index 9b74f16..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/IJavaParser.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.Location; - -import lombok.ast.Node; - -/** - * A wrapper for a Java parser. This allows tools integrating lint to map directly - * to builtin services, such as already-parsed data structures in Java editors. - * <p/> - * <b>NOTE: This is not a or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -public interface IJavaParser { - /** - * Parse the file pointed to by the given context. - * - * @param context the context pointing to the file to be parsed, typically - * via {@link Context#getContents()} but the file handle ( - * {@link Context#file} can also be used to map to an existing - * editor buffer in the surrounding tool, etc) - * @return the compilation unit node for the file - */ - @Nullable - Node parseJava(@NonNull JavaContext context); - - /** - * Returns a {@link Location} for the given node - * - * @param context information about the file being parsed - * @param node the node to create a location for - * @return a location for the given node - */ - @NonNull - Location getLocation(@NonNull JavaContext context, @NonNull Node node); - - /** - * Creates a light-weight handle to a location for the given node. It can be - * turned into a full fledged location by - * {@link com.android.tools.lint.detector.api.Location.Handle#resolve()}. - * - * @param context the context providing the node - * @param node the node (element or attribute) to create a location handle - * for - * @return a location handle - */ - @NonNull - Location.Handle createLocationHandle(@NonNull JavaContext context, @NonNull Node node); - - /** - * Dispose any data structures held for the given context. - * @param context information about the file previously parsed - * @param compilationUnit the compilation unit being disposed - */ - void dispose(@NonNull JavaContext context, @NonNull Node compilationUnit); -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/IssueRegistry.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/IssueRegistry.java deleted file mode 100644 index 0b8b141..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/IssueRegistry.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.annotations.VisibleForTesting; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Scope; -import com.android.tools.lint.detector.api.Severity; -import com.google.common.annotations.Beta; -import com.google.common.collect.Maps; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** Registry which provides a list of checks to be performed on an Android project - * <p> - * <b>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.</b> - */ -@Beta -public abstract class IssueRegistry { - private static List<Category> sCategories; - private static Map<String, Issue> sIdToIssue; - private static Map<EnumSet<Scope>, List<Issue>> sScopeIssues = Maps.newHashMap(); - - /** - * Creates a new {@linkplain IssueRegistry} - */ - protected IssueRegistry() { - } - - /** - * Issue reported by lint (not a specific detector) when it cannot even - * parse an XML file prior to analysis - */ - @NonNull - public static final Issue PARSER_ERROR = Issue.create( - "ParserError", //$NON-NLS-1$ - "Finds files that contain fatal parser errors", - "Lint will ignore any files that contain fatal parsing errors. These may contain " + - "other errors, or contain code which affects issues in other files.", - Category.CORRECTNESS, - 10, - Severity.ERROR, - Detector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** - * Issue reported by lint for various other issues which prevents lint from - * running normally when it's not necessarily an error in the user's code base. - */ - @NonNull - public static final Issue LINT_ERROR = Issue.create( - "LintError", //$NON-NLS-1$ - "Issues related to running lint itself, such as failure to read files, etc", - "This issue type represents a problem running lint itself. Examples include " + - "failure to find bytecode for source files (which means certain detectors " + - "could not be run), parsing errors in lint configuration files, etc." + - "\n" + - "These errors are not errors in your own code, but they are shown to make " + - "it clear that some checks were not completed.", - - Category.LINT, - 10, - Severity.ERROR, - Detector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** - * Returns the list of issues that can be found by all known detectors. - * - * @return the list of issues to be checked (including those that may be - * disabled!) - */ - @NonNull - public abstract List<Issue> getIssues(); - - /** - * Returns all available issues of a given scope (regardless of whether - * they are actually enabled for a given configuration etc) - * - * @param scope the applicable scope set - * @return a list of issues - */ - @NonNull - private List<Issue> getIssuesForScope(@NonNull EnumSet<Scope> scope) { - List<Issue> list = sScopeIssues.get(scope); - if (list == null) { - List<Issue> issues = getIssues(); - if (scope.equals(Scope.ALL)) { - list = issues; - } else { - int initialSize = 12; - if (scope.contains(Scope.RESOURCE_FILE)) { - initialSize += 50; - } - if (scope.contains(Scope.JAVA_FILE)) { - initialSize += 12; - } - if (scope.contains(Scope.CLASS_FILE)) { - initialSize += 12; - } - list = new ArrayList<Issue>(initialSize); - for (Issue issue : issues) { - // Determine if the scope matches - if (issue.isAdequate(scope)) { - list.add(issue); - } - } - } - sScopeIssues.put(scope, list); - } - - return list; - } - - /** - * Creates a list of detectors applicable to the given scope, and with the - * given configuration. - * - * @param client the client to report errors to - * @param configuration the configuration to look up which issues are - * enabled etc from - * @param scope the scope for the analysis, to filter out detectors that - * require wider analysis than is currently being performed - * @param scopeToDetectors an optional map which (if not null) will be - * filled by this method to contain mappings from each scope to - * the applicable detectors for that scope - * @return a list of new detector instances - */ - @NonNull - final List<? extends Detector> createDetectors( - @NonNull LintClient client, - @NonNull Configuration configuration, - @NonNull EnumSet<Scope> scope, - @Nullable Map<Scope, List<Detector>> scopeToDetectors) { - - List<Issue> issues = getIssuesForScope(scope); - if (issues.isEmpty()) { - return Collections.emptyList(); - } - - Set<Class<? extends Detector>> detectorClasses = new HashSet<Class<? extends Detector>>(); - Map<Class<? extends Detector>, EnumSet<Scope>> detectorToScope = - new HashMap<Class<? extends Detector>, EnumSet<Scope>>(); - - for (Issue issue : issues) { - Class<? extends Detector> detectorClass = issue.getDetectorClass(); - EnumSet<Scope> issueScope = issue.getScope(); - if (!detectorClasses.contains(detectorClass)) { - // Determine if the issue is enabled - if (!configuration.isEnabled(issue)) { - continue; - } - - assert issue.isAdequate(scope); // Ensured by getIssuesForScope above - - detectorClass = client.replaceDetector(detectorClass); - - assert detectorClass != null : issue.getId(); - detectorClasses.add(detectorClass); - } - - if (scopeToDetectors != null) { - EnumSet<Scope> s = detectorToScope.get(detectorClass); - if (s == null) { - detectorToScope.put(detectorClass, issueScope); - } else if (!s.containsAll(issueScope)) { - EnumSet<Scope> union = EnumSet.copyOf(s); - union.addAll(issueScope); - detectorToScope.put(detectorClass, union); - } - } - } - - List<Detector> detectors = new ArrayList<Detector>(detectorClasses.size()); - for (Class<? extends Detector> clz : detectorClasses) { - try { - Detector detector = clz.newInstance(); - detectors.add(detector); - - if (scopeToDetectors != null) { - EnumSet<Scope> union = detectorToScope.get(clz); - for (Scope s : union) { - List<Detector> list = scopeToDetectors.get(s); - if (list == null) { - list = new ArrayList<Detector>(); - scopeToDetectors.put(s, list); - } - list.add(detector); - } - - } - } catch (Throwable t) { - client.log(t, "Can't initialize detector %1$s", clz.getName()); //$NON-NLS-1$ - } - } - - return detectors; - } - - /** - * Returns true if the given id represents a valid issue id - * - * @param id the id to be checked - * @return true if the given id is valid - */ - public final boolean isIssueId(@NonNull String id) { - return getIssue(id) != null; - } - - /** - * Returns true if the given category is a valid category - * - * @param name the category name to be checked - * @return true if the given string is a valid category - */ - public final boolean isCategoryName(@NonNull String name) { - for (Category category : getCategories()) { - if (category.getName().equals(name) || category.getFullName().equals(name)) { - return true; - } - } - - return false; - } - - /** - * Returns the available categories - * - * @return an iterator for all the categories, never null - */ - @NonNull - public List<Category> getCategories() { - if (sCategories == null) { - final Set<Category> categories = new HashSet<Category>(); - for (Issue issue : getIssues()) { - categories.add(issue.getCategory()); - } - List<Category> sorted = new ArrayList<Category>(categories); - Collections.sort(sorted); - sCategories = Collections.unmodifiableList(sorted); - } - - return sCategories; - } - - /** - * Returns the issue for the given id, or null if it's not a valid id - * - * @param id the id to be checked - * @return the corresponding issue, or null - */ - @Nullable - public final Issue getIssue(@NonNull String id) { - if (sIdToIssue == null) { - List<Issue> issues = getIssues(); - sIdToIssue = new HashMap<String, Issue>(issues.size()); - for (Issue issue : issues) { - sIdToIssue.put(issue.getId(), issue); - } - - sIdToIssue.put(PARSER_ERROR.getId(), PARSER_ERROR); - sIdToIssue.put(LINT_ERROR.getId(), LINT_ERROR); - } - return sIdToIssue.get(id); - } - - /** - * Reset the registry such that it recomputes its available issues. - * <p> - * NOTE: This is only intended for testing purposes. - */ - @VisibleForTesting - protected static void reset() { - sIdToIssue = null; - sCategories = null; - sScopeIssues = Maps.newHashMap(); - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java deleted file mode 100644 index 81a0339..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java +++ /dev/null @@ -1,1195 +0,0 @@ -/* - * 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.ANDROID_PKG; -import static com.android.SdkConstants.R_CLASS; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.JavaScanner; -import com.android.tools.lint.detector.api.Detector.XmlScanner; -import com.android.tools.lint.detector.api.JavaContext; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import lombok.ast.AlternateConstructorInvocation; -import lombok.ast.Annotation; -import lombok.ast.AnnotationDeclaration; -import lombok.ast.AnnotationElement; -import lombok.ast.AnnotationMethodDeclaration; -import lombok.ast.AnnotationValueArray; -import lombok.ast.ArrayAccess; -import lombok.ast.ArrayCreation; -import lombok.ast.ArrayDimension; -import lombok.ast.ArrayInitializer; -import lombok.ast.Assert; -import lombok.ast.AstVisitor; -import lombok.ast.BinaryExpression; -import lombok.ast.Block; -import lombok.ast.BooleanLiteral; -import lombok.ast.Break; -import lombok.ast.Case; -import lombok.ast.Cast; -import lombok.ast.Catch; -import lombok.ast.CharLiteral; -import lombok.ast.ClassDeclaration; -import lombok.ast.ClassLiteral; -import lombok.ast.Comment; -import lombok.ast.CompilationUnit; -import lombok.ast.ConstructorDeclaration; -import lombok.ast.ConstructorInvocation; -import lombok.ast.Continue; -import lombok.ast.Default; -import lombok.ast.DoWhile; -import lombok.ast.EmptyDeclaration; -import lombok.ast.EmptyStatement; -import lombok.ast.EnumConstant; -import lombok.ast.EnumDeclaration; -import lombok.ast.EnumTypeBody; -import lombok.ast.Expression; -import lombok.ast.ExpressionStatement; -import lombok.ast.FloatingPointLiteral; -import lombok.ast.For; -import lombok.ast.ForEach; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Identifier; -import lombok.ast.If; -import lombok.ast.ImportDeclaration; -import lombok.ast.InlineIfExpression; -import lombok.ast.InstanceInitializer; -import lombok.ast.InstanceOf; -import lombok.ast.IntegralLiteral; -import lombok.ast.InterfaceDeclaration; -import lombok.ast.KeywordModifier; -import lombok.ast.LabelledStatement; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Modifiers; -import lombok.ast.Node; -import lombok.ast.NormalTypeBody; -import lombok.ast.NullLiteral; -import lombok.ast.PackageDeclaration; -import lombok.ast.Return; -import lombok.ast.Select; -import lombok.ast.StaticInitializer; -import lombok.ast.StringLiteral; -import lombok.ast.Super; -import lombok.ast.SuperConstructorInvocation; -import lombok.ast.Switch; -import lombok.ast.Synchronized; -import lombok.ast.This; -import lombok.ast.Throw; -import lombok.ast.Try; -import lombok.ast.TypeReference; -import lombok.ast.TypeReferencePart; -import lombok.ast.TypeVariable; -import lombok.ast.UnaryExpression; -import lombok.ast.VariableDeclaration; -import lombok.ast.VariableDefinition; -import lombok.ast.VariableDefinitionEntry; -import lombok.ast.VariableReference; -import lombok.ast.While; - - -/** - * Specialized visitor for running detectors on a Java AST. - * It operates in three phases: - * <ol> - * <li> First, it computes a set of maps where it generates a map from each - * significant AST attribute (such as method call names) to a list - * of detectors to consult whenever that attribute is encountered. - * Examples of "attributes" are method names, Android resource identifiers, - * and general AST node types such as "cast" nodes etc. These are - * defined on the {@link JavaScanner} interface. - * <li> Second, it iterates over the document a single time, delegating to - * the detectors found at each relevant AST attribute. - * <li> Finally, it calls the remaining visitors (those that need to process a - * whole document on their own). - * </ol> - * It also notifies all the detectors before and after the document is processed - * such that they can do pre- and post-processing. - */ -public class JavaVisitor { - /** Default size of lists holding detectors of the same type for a given node type */ - private static final int SAME_TYPE_COUNT = 8; - - private final Map<String, List<VisitingDetector>> mMethodDetectors = - new HashMap<String, List<VisitingDetector>>(); - private final List<VisitingDetector> mResourceFieldDetectors = - new ArrayList<VisitingDetector>(); - private final List<VisitingDetector> mAllDetectors; - private final List<VisitingDetector> mFullTreeDetectors; - private final Map<Class<? extends Node>, List<VisitingDetector>> mNodeTypeDetectors = - new HashMap<Class<? extends Node>, List<VisitingDetector>>(); - private final IJavaParser mParser; - - JavaVisitor(@NonNull IJavaParser parser, @NonNull List<Detector> detectors) { - mParser = parser; - mAllDetectors = new ArrayList<VisitingDetector>(detectors.size()); - mFullTreeDetectors = new ArrayList<VisitingDetector>(detectors.size()); - - for (Detector detector : detectors) { - VisitingDetector v = new VisitingDetector(detector, (JavaScanner) detector); - mAllDetectors.add(v); - - List<Class<? extends Node>> nodeTypes = detector.getApplicableNodeTypes(); - if (nodeTypes != null) { - for (Class<? extends Node> type : nodeTypes) { - List<VisitingDetector> list = mNodeTypeDetectors.get(type); - if (list == null) { - list = new ArrayList<VisitingDetector>(SAME_TYPE_COUNT); - mNodeTypeDetectors.put(type, list); - } - list.add(v); - } - } - - List<String> names = detector.getApplicableMethodNames(); - if (names != null) { - // not supported in Java visitors; adding a method invocation node is trivial - // for that case. - assert names != XmlScanner.ALL; - - for (String name : names) { - List<VisitingDetector> list = mMethodDetectors.get(name); - if (list == null) { - list = new ArrayList<VisitingDetector>(SAME_TYPE_COUNT); - mMethodDetectors.put(name, list); - } - list.add(v); - } - } - - if (detector.appliesToResourceRefs()) { - mResourceFieldDetectors.add(v); - } else if ((names == null || names.isEmpty()) - && (nodeTypes == null || nodeTypes.isEmpty())) { - mFullTreeDetectors.add(v); - } - } - } - - void visitFile(@NonNull JavaContext context, @NonNull File file) { - context.parser = mParser; - - Node compilationUnit = null; - try { - compilationUnit = mParser.parseJava(context); - if (compilationUnit == null) { - // No need to log this; the parser should be reporting - // a full warning (such as IssueRegistry#PARSER_ERROR) - // with details, location, etc. - return; - } - context.compilationUnit = compilationUnit; - - for (VisitingDetector v : mAllDetectors) { - v.setContext(context); - v.getDetector().beforeCheckFile(context); - } - - for (VisitingDetector v : mFullTreeDetectors) { - AstVisitor visitor = v.getVisitor(); - if (visitor != null) { - compilationUnit.accept(visitor); - } - } - - if (!mMethodDetectors.isEmpty() || !mResourceFieldDetectors.isEmpty()) { - AstVisitor visitor = new DelegatingJavaVisitor(context); - compilationUnit.accept(visitor); - } else if (!mNodeTypeDetectors.isEmpty()) { - AstVisitor visitor = new DispatchVisitor(); - compilationUnit.accept(visitor); - } - - for (VisitingDetector v : mAllDetectors) { - v.getDetector().afterCheckFile(context); - } - } finally { - if (compilationUnit != null) { - mParser.dispose(context, compilationUnit); - } - } - } - - private static class VisitingDetector { - private AstVisitor mVisitor; // construct lazily, and clear out on context switch! - private JavaContext mContext; - public final Detector mDetector; - public final JavaScanner mJavaScanner; - - public VisitingDetector(@NonNull Detector detector, @NonNull JavaScanner javaScanner) { - mDetector = detector; - mJavaScanner = javaScanner; - } - - @NonNull - public Detector getDetector() { - return mDetector; - } - - @NonNull - public JavaScanner getJavaScanner() { - return mJavaScanner; - } - - public void setContext(@NonNull JavaContext context) { - mContext = context; - - // The visitors are one-per-context, so clear them out here and construct - // lazily only if needed - mVisitor = null; - } - - @NonNull - AstVisitor getVisitor() { - if (mVisitor == null) { - mVisitor = mDetector.createJavaVisitor(mContext); - if (mVisitor == null) { - mVisitor = new ForwardingAstVisitor() { - }; - } - } - return mVisitor; - } - } - - /** - * Generic dispatcher which visits all nodes (once) and dispatches to - * specific visitors for each node. Each visitor typically only wants to - * look at a small part of a tree, such as a method call or a class - * declaration, so this means we avoid visiting all "uninteresting" nodes in - * the tree repeatedly. - */ - private class DispatchVisitor extends AstVisitor { - @Override - public void endVisit(Node node) { - for (VisitingDetector v : mAllDetectors) { - v.getVisitor().endVisit(node); - } - } - - @Override - public boolean visitAlternateConstructorInvocation(AlternateConstructorInvocation node) { - List<VisitingDetector> list = - mNodeTypeDetectors.get(AlternateConstructorInvocation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAlternateConstructorInvocation(node); - } - } - return false; - } - - @Override - public boolean visitAnnotation(Annotation node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Annotation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAnnotation(node); - } - } - return false; - } - - @Override - public boolean visitAnnotationDeclaration(AnnotationDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(AnnotationDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAnnotationDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitAnnotationElement(AnnotationElement node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(AnnotationElement.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAnnotationElement(node); - } - } - return false; - } - - @Override - public boolean visitAnnotationMethodDeclaration(AnnotationMethodDeclaration node) { - List<VisitingDetector> list = - mNodeTypeDetectors.get(AnnotationMethodDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAnnotationMethodDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitAnnotationValueArray(AnnotationValueArray node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(AnnotationValueArray.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAnnotationValueArray(node); - } - } - return false; - } - - @Override - public boolean visitArrayAccess(ArrayAccess node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ArrayAccess.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitArrayAccess(node); - } - } - return false; - } - - @Override - public boolean visitArrayCreation(ArrayCreation node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ArrayCreation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitArrayCreation(node); - } - } - return false; - } - - @Override - public boolean visitArrayDimension(ArrayDimension node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ArrayDimension.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitArrayDimension(node); - } - } - return false; - } - - @Override - public boolean visitArrayInitializer(ArrayInitializer node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ArrayInitializer.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitArrayInitializer(node); - } - } - return false; - } - - @Override - public boolean visitAssert(Assert node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Assert.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitAssert(node); - } - } - return false; - } - - @Override - public boolean visitBinaryExpression(BinaryExpression node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(BinaryExpression.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitBinaryExpression(node); - } - } - return false; - } - - @Override - public boolean visitBlock(Block node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Block.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitBlock(node); - } - } - return false; - } - - @Override - public boolean visitBooleanLiteral(BooleanLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(BooleanLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitBooleanLiteral(node); - } - } - return false; - } - - @Override - public boolean visitBreak(Break node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Break.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitBreak(node); - } - } - return false; - } - - @Override - public boolean visitCase(Case node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Case.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitCase(node); - } - } - return false; - } - - @Override - public boolean visitCast(Cast node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Cast.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitCast(node); - } - } - return false; - } - - @Override - public boolean visitCatch(Catch node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Catch.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitCatch(node); - } - } - return false; - } - - @Override - public boolean visitCharLiteral(CharLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(CharLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitCharLiteral(node); - } - } - return false; - } - - @Override - public boolean visitClassDeclaration(ClassDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ClassDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitClassDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitClassLiteral(ClassLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ClassLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitClassLiteral(node); - } - } - return false; - } - - @Override - public boolean visitComment(Comment node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Comment.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitComment(node); - } - } - return false; - } - - @Override - public boolean visitCompilationUnit(CompilationUnit node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(CompilationUnit.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitCompilationUnit(node); - } - } - return false; - } - - @Override - public boolean visitConstructorDeclaration(ConstructorDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ConstructorDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitConstructorDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitConstructorInvocation(ConstructorInvocation node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ConstructorInvocation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitConstructorInvocation(node); - } - } - return false; - } - - @Override - public boolean visitContinue(Continue node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Continue.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitContinue(node); - } - } - return false; - } - - @Override - public boolean visitDefault(Default node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Default.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitDefault(node); - } - } - return false; - } - - @Override - public boolean visitDoWhile(DoWhile node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(DoWhile.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitDoWhile(node); - } - } - return false; - } - - @Override - public boolean visitEmptyDeclaration(EmptyDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(EmptyDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitEmptyDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitEmptyStatement(EmptyStatement node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(EmptyStatement.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitEmptyStatement(node); - } - } - return false; - } - - @Override - public boolean visitEnumConstant(EnumConstant node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(EnumConstant.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitEnumConstant(node); - } - } - return false; - } - - @Override - public boolean visitEnumDeclaration(EnumDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(EnumDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitEnumDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitEnumTypeBody(EnumTypeBody node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(EnumTypeBody.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitEnumTypeBody(node); - } - } - return false; - } - - @Override - public boolean visitExpressionStatement(ExpressionStatement node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ExpressionStatement.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitExpressionStatement(node); - } - } - return false; - } - - @Override - public boolean visitFloatingPointLiteral(FloatingPointLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(FloatingPointLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitFloatingPointLiteral(node); - } - } - return false; - } - - @Override - public boolean visitFor(For node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(For.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitFor(node); - } - } - return false; - } - - @Override - public boolean visitForEach(ForEach node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ForEach.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitForEach(node); - } - } - return false; - } - - @Override - public boolean visitIdentifier(Identifier node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Identifier.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitIdentifier(node); - } - } - return false; - } - - @Override - public boolean visitIf(If node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(If.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitIf(node); - } - } - return false; - } - - @Override - public boolean visitImportDeclaration(ImportDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(ImportDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitImportDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitInlineIfExpression(InlineIfExpression node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(InlineIfExpression.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitInlineIfExpression(node); - } - } - return false; - } - - @Override - public boolean visitInstanceInitializer(InstanceInitializer node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(InstanceInitializer.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitInstanceInitializer(node); - } - } - return false; - } - - @Override - public boolean visitInstanceOf(InstanceOf node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(InstanceOf.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitInstanceOf(node); - } - } - return false; - } - - @Override - public boolean visitIntegralLiteral(IntegralLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(IntegralLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitIntegralLiteral(node); - } - } - return false; - } - - @Override - public boolean visitInterfaceDeclaration(InterfaceDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(InterfaceDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitInterfaceDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitKeywordModifier(KeywordModifier node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(KeywordModifier.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitKeywordModifier(node); - } - } - return false; - } - - @Override - public boolean visitLabelledStatement(LabelledStatement node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(LabelledStatement.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitLabelledStatement(node); - } - } - return false; - } - - @Override - public boolean visitMethodDeclaration(MethodDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(MethodDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitMethodDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(MethodInvocation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitMethodInvocation(node); - } - } - return false; - } - - @Override - public boolean visitModifiers(Modifiers node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Modifiers.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitModifiers(node); - } - } - return false; - } - - @Override - public boolean visitNormalTypeBody(NormalTypeBody node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(NormalTypeBody.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitNormalTypeBody(node); - } - } - return false; - } - - @Override - public boolean visitNullLiteral(NullLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(NullLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitNullLiteral(node); - } - } - return false; - } - - @Override - public boolean visitPackageDeclaration(PackageDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(PackageDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitPackageDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitParseArtefact(Node node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Node.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitParseArtefact(node); - } - } - return false; - } - - @Override - public boolean visitReturn(Return node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Return.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitReturn(node); - } - } - return false; - } - - @Override - public boolean visitSelect(Select node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Select.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitSelect(node); - } - } - return false; - } - - @Override - public boolean visitStaticInitializer(StaticInitializer node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(StaticInitializer.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitStaticInitializer(node); - } - } - return false; - } - - @Override - public boolean visitStringLiteral(StringLiteral node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(StringLiteral.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitStringLiteral(node); - } - } - return false; - } - - @Override - public boolean visitSuper(Super node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Super.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitSuper(node); - } - } - return false; - } - - @Override - public boolean visitSuperConstructorInvocation(SuperConstructorInvocation node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(SuperConstructorInvocation.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitSuperConstructorInvocation(node); - } - } - return false; - } - - @Override - public boolean visitSwitch(Switch node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Switch.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitSwitch(node); - } - } - return false; - } - - @Override - public boolean visitSynchronized(Synchronized node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Synchronized.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitSynchronized(node); - } - } - return false; - } - - @Override - public boolean visitThis(This node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(This.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitThis(node); - } - } - return false; - } - - @Override - public boolean visitThrow(Throw node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Throw.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitThrow(node); - } - } - return false; - } - - @Override - public boolean visitTry(Try node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(Try.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitTry(node); - } - } - return false; - } - - @Override - public boolean visitTypeReference(TypeReference node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(TypeReference.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitTypeReference(node); - } - } - return false; - } - - @Override - public boolean visitTypeReferencePart(TypeReferencePart node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(TypeReferencePart.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitTypeReferencePart(node); - } - } - return false; - } - - @Override - public boolean visitTypeVariable(TypeVariable node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(TypeVariable.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitTypeVariable(node); - } - } - return false; - } - - @Override - public boolean visitUnaryExpression(UnaryExpression node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(UnaryExpression.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitUnaryExpression(node); - } - } - return false; - } - - @Override - public boolean visitVariableDeclaration(VariableDeclaration node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(VariableDeclaration.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitVariableDeclaration(node); - } - } - return false; - } - - @Override - public boolean visitVariableDefinition(VariableDefinition node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(VariableDefinition.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitVariableDefinition(node); - } - } - return false; - } - - @Override - public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(VariableDefinitionEntry.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitVariableDefinitionEntry(node); - } - } - return false; - } - - @Override - public boolean visitVariableReference(VariableReference node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(VariableReference.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitVariableReference(node); - } - } - return false; - } - - @Override - public boolean visitWhile(While node) { - List<VisitingDetector> list = mNodeTypeDetectors.get(While.class); - if (list != null) { - for (VisitingDetector v : list) { - v.getVisitor().visitWhile(node); - } - } - return false; - } - } - - /** Performs common AST searches for method calls and R-type-field references. - * Note that this is a specialized form of the {@link DispatchVisitor}. */ - private class DelegatingJavaVisitor extends DispatchVisitor { - private final JavaContext mContext; - private final boolean mVisitResources; - private final boolean mVisitMethods; - - public DelegatingJavaVisitor(JavaContext context) { - mContext = context; - - mVisitMethods = !mMethodDetectors.isEmpty(); - mVisitResources = !mResourceFieldDetectors.isEmpty(); - } - - @Override - public boolean visitSelect(Select node) { - if (mVisitResources) { - // R.type.name - if (node.astOperand() instanceof Select) { - Select select = (Select) node.astOperand(); - if (select.astOperand() instanceof VariableReference) { - VariableReference reference = (VariableReference) select.astOperand(); - if (reference.astIdentifier().astValue().equals(R_CLASS)) { - String type = select.astIdentifier().astValue(); - String name = node.astIdentifier().astValue(); - - // R -could- be referenced locally and really have been - // imported as "import android.R;" in the import statements, - // but this is not recommended (and in fact there's a specific - // lint rule warning against it) - boolean isFramework = false; - - for (VisitingDetector v : mResourceFieldDetectors) { - JavaScanner detector = v.getJavaScanner(); - detector.visitResourceReference(mContext, v.getVisitor(), - node, type, name, isFramework); - } - - return super.visitSelect(node); - } - } - } - - // Arbitrary packages -- android.R.type.name, foo.bar.R.type.name - if (node.astIdentifier().astValue().equals(R_CLASS)) { - Node parent = node.getParent(); - if (parent instanceof Select) { - Node grandParent = parent.getParent(); - if (grandParent instanceof Select) { - Select select = (Select) grandParent; - String name = select.astIdentifier().astValue(); - Expression typeOperand = select.astOperand(); - if (typeOperand instanceof Select) { - Select typeSelect = (Select) typeOperand; - String type = typeSelect.astIdentifier().astValue(); - boolean isFramework = - node.astIdentifier().astValue().equals(ANDROID_PKG); - for (VisitingDetector v : mResourceFieldDetectors) { - JavaScanner detector = v.getJavaScanner(); - detector.visitResourceReference(mContext, v.getVisitor(), - node, type, name, isFramework); - } - } - } - } - } - } - - return super.visitSelect(node); - } - - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (mVisitMethods) { - String methodName = node.astName().getDescription(); - List<VisitingDetector> list = mMethodDetectors.get(methodName); - if (list != null) { - for (VisitingDetector v : list) { - v.getJavaScanner().visitMethod(mContext, v.getVisitor(), node); - } - } - } - - return super.visitMethodInvocation(node); - } - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintClient.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintClient.java deleted file mode 100644 index b8118b7..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintClient.java +++ /dev/null @@ -1,691 +0,0 @@ -/* - * 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.CLASS_FOLDER; -import static com.android.SdkConstants.DOT_JAR; -import static com.android.SdkConstants.GEN_FOLDER; -import static com.android.SdkConstants.LIBS_FOLDER; -import static com.android.SdkConstants.RES_FOLDER; -import static com.android.SdkConstants.SRC_FOLDER; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.SdkManager; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Project; -import com.android.tools.lint.detector.api.Severity; -import com.android.utils.StdLogger; -import com.android.utils.StdLogger.Level; -import com.google.common.annotations.Beta; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.io.Files; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; - -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -/** - * Information about the tool embedding the lint analyzer. IDEs and other tools - * implementing lint support will extend this to integrate logging, displaying errors, - * etc. - * <p/> - * <b>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.</b> - */ -@Beta -public abstract class LintClient { - private static final String PROP_BIN_DIR = "com.android.tools.lint.bindir"; //$NON-NLS-1$ - - /** - * Returns a configuration for use by the given project. The configuration - * provides information about which issues are enabled, any customizations - * to the severity of an issue, etc. - * <p> - * By default this method returns a {@link DefaultConfiguration}. - * - * @param project the project to obtain a configuration for - * @return a configuration, never null. - */ - public Configuration getConfiguration(@NonNull Project project) { - return DefaultConfiguration.create(this, project, null); - } - - /** - * Report the given issue. This method will only be called if the configuration - * provided by {@link #getConfiguration(Project)} has reported the corresponding - * issue as enabled and has not filtered out the issue with its - * {@link Configuration#ignore(Context, Issue, Location, String, Object)} method. - * <p> - * - * @param context the context used by the detector when the issue was found - * @param issue the issue that was found - * @param severity the severity of the issue - * @param location the location of the issue - * @param message the associated user message - * @param data optional extra data for a discovered issue, or null. The - * content depends on the specific issue. Detectors can pass - * extra info here which automatic fix tools etc can use to - * extract relevant information instead of relying on parsing the - * error message text. See each detector for details on which - * data if any is supplied for a given issue. - */ - public abstract void report( - @NonNull Context context, - @NonNull Issue issue, - @NonNull Severity severity, - @Nullable Location location, - @NonNull String message, - @Nullable Object data); - - /** - * Send an exception or error message (with warning severity) to the log - * - * @param exception the exception, possibly null - * @param format the error message using {@link String#format} syntax, possibly null - * (though in that case the exception should not be null) - * @param args any arguments for the format string - */ - public void log( - @Nullable Throwable exception, - @Nullable String format, - @Nullable Object... args) { - log(Severity.WARNING, exception, format, args); - } - - /** - * Send an exception or error message to the log - * - * @param severity the severity of the warning - * @param exception the exception, possibly null - * @param format the error message using {@link String#format} syntax, possibly null - * (though in that case the exception should not be null) - * @param args any arguments for the format string - */ - public abstract void log( - @NonNull Severity severity, - @Nullable Throwable exception, - @Nullable String format, - @Nullable Object... args); - - /** - * Returns a {@link IDomParser} to use to parse XML - * - * @return a new {@link IDomParser}, or null if this client does not support - * XML analysis - */ - @Nullable - public abstract IDomParser getDomParser(); - - /** - * Returns a {@link IJavaParser} to use to parse Java - * - * @return a new {@link IJavaParser}, or null if this client does not - * support Java analysis - */ - @Nullable - public abstract IJavaParser getJavaParser(); - - /** - * Returns an optimal detector, if applicable. By default, just returns the - * original detector, but tools can replace detectors using this hook with a version - * that takes advantage of native capabilities of the tool. - * - * @param detectorClass the class of the detector to be replaced - * @return the new detector class, or just the original detector (not null) - */ - @NonNull - public Class<? extends Detector> replaceDetector( - @NonNull Class<? extends Detector> detectorClass) { - return detectorClass; - } - - /** - * Reads the given text file and returns the content as a string - * - * @param file the file to read - * @return the string to return, never null (will be empty if there is an - * I/O error) - */ - @NonNull - public abstract String readFile(@NonNull File file); - - /** - * Reads the given binary file and returns the content as a byte array. - * By default this method will read the bytes from the file directly, - * but this can be customized by a client if for example I/O could be - * held in memory and not flushed to disk yet. - * - * @param file the file to read - * @return the bytes in the file, never null - * @throws IOException if the file does not exist, or if the file cannot be - * read for some reason - */ - @NonNull - public byte[] readBytes(@NonNull File file) throws IOException { - return Files.toByteArray(file); - } - - /** - * Returns the list of source folders for Java source files - * - * @param project the project to look up Java source file locations for - * @return a list of source folders to search for .java files - */ - @NonNull - public List<File> getJavaSourceFolders(@NonNull Project project) { - return getClassPath(project).getSourceFolders(); - } - - /** - * Returns the list of output folders for class files - * - * @param project the project to look up class file locations for - * @return a list of output folders to search for .class files - */ - @NonNull - public List<File> getJavaClassFolders(@NonNull Project project) { - return getClassPath(project).getClassFolders(); - - } - - /** - * Returns the list of Java libraries - * - * @param project the project to look up jar dependencies for - * @return a list of jar dependencies containing .class files - */ - @NonNull - public List<File> getJavaLibraries(@NonNull Project project) { - return getClassPath(project).getLibraries(); - } - - /** - * Returns the resource folders. - * - * @param project the project to look up the resource folder for - * @return a list of files pointing to the resource folders, possibly empty - */ - @NonNull - public List<File> getResourceFolders(@NonNull Project project) { - File res = new File(project.getDir(), RES_FOLDER); - if (res.exists()) { - return Collections.singletonList(res); - } - - return Collections.emptyList(); - } - - /** - * Returns the {@link SdkInfo} to use for the given project. - * - * @param project the project to look up an {@link SdkInfo} for - * @return an {@link SdkInfo} for the project - */ - @NonNull - public SdkInfo getSdkInfo(@NonNull Project project) { - // By default no per-platform SDK info - return new DefaultSdkInfo(); - } - - /** - * Returns a suitable location for storing cache files. Note that the - * directory may not exist. - * - * @param create if true, attempt to create the cache dir if it does not - * exist - * @return a suitable location for storing cache files, which may be null if - * the create flag was false, or if for some reason the directory - * could not be created - */ - @Nullable - public File getCacheDir(boolean create) { - String home = System.getProperty("user.home"); - String relative = ".android" + File.separator + "cache"; //$NON-NLS-1$ //$NON-NLS-2$ - File dir = new File(home, relative); - if (create && !dir.exists()) { - if (!dir.mkdirs()) { - return null; - } - } - return dir; - } - - /** - * Returns the File corresponding to the system property or the environment variable - * for {@link #PROP_BIN_DIR}. - * This property is typically set by the SDK/tools/lint[.bat] wrapper. - * It denotes the path of the wrapper on disk. - * - * @return A new File corresponding to {@link LintClient#PROP_BIN_DIR} or null. - */ - @Nullable - private static File getLintBinDir() { - // First check the Java properties (e.g. set using "java -jar ... -Dname=value") - String path = System.getProperty(PROP_BIN_DIR); - if (path == null || path.isEmpty()) { - // If not found, check environment variables. - path = System.getenv(PROP_BIN_DIR); - } - if (path != null && !path.isEmpty()) { - return new File(path); - } - return null; - } - - /** - * Returns the File pointing to the user's SDK install area. This is generally - * the root directory containing the lint tool (but also platforms/ etc). - * - * @return a file pointing to the user's install area - */ - @Nullable - public File getSdkHome() { - File binDir = getLintBinDir(); - if (binDir != null) { - assert binDir.getName().equals("tools"); - - File root = binDir.getParentFile(); - if (root != null && root.isDirectory()) { - return root; - } - } - - String home = System.getenv("ANDROID_HOME"); //$NON-NLS-1$ - if (home != null) { - return new File(home); - } - - return null; - } - - /** - * Locates an SDK resource (relative to the SDK root directory). - * <p> - * TODO: Consider switching to a {@link URL} return type instead. - * - * @param relativePath A relative path (using {@link File#separator} to - * separate path components) to the given resource - * @return a {@link File} pointing to the resource, or null if it does not - * exist - */ - @Nullable - public File findResource(@NonNull String relativePath) { - File top = getSdkHome(); - if (top == null) { - throw new IllegalArgumentException("Lint must be invoked with the System property " - + PROP_BIN_DIR + " pointing to the ANDROID_SDK tools directory"); - } - - File file = new File(top, relativePath); - if (file.exists()) { - return file; - } else { - return null; - } - } - - private Map<Project, ClassPathInfo> mProjectInfo; - - /** - * Information about class paths (sources, class files and libraries) - * usually associated with a project. - */ - protected static class ClassPathInfo { - private final List<File> mClassFolders; - private final List<File> mSourceFolders; - private final List<File> mLibraries; - - public ClassPathInfo( - @NonNull List<File> sourceFolders, - @NonNull List<File> classFolders, - @NonNull List<File> libraries) { - mSourceFolders = sourceFolders; - mClassFolders = classFolders; - mLibraries = libraries; - } - - @NonNull - public List<File> getSourceFolders() { - return mSourceFolders; - } - - @NonNull - public List<File> getClassFolders() { - return mClassFolders; - } - - @NonNull - public List<File> getLibraries() { - return mLibraries; - } - } - - /** - * Considers the given project as an Eclipse project and returns class path - * information for the project - the source folder(s), the output folder and - * any libraries. - * <p> - * Callers will not cache calls to this method, so if it's expensive to compute - * the classpath info, this method should perform its own caching. - * - * @param project the project to look up class path info for - * @return a class path info object, never null - */ - @NonNull - protected ClassPathInfo getClassPath(@NonNull Project project) { - ClassPathInfo info; - if (mProjectInfo == null) { - mProjectInfo = Maps.newHashMap(); - info = null; - } else { - info = mProjectInfo.get(project); - } - - if (info == null) { - List<File> sources = new ArrayList<File>(2); - List<File> classes = new ArrayList<File>(1); - List<File> libraries = new ArrayList<File>(); - - File projectDir = project.getDir(); - File classpathFile = new File(projectDir, ".classpath"); //$NON-NLS-1$ - if (classpathFile.exists()) { - String classpathXml = readFile(classpathFile); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - InputSource is = new InputSource(new StringReader(classpathXml)); - factory.setNamespaceAware(false); - factory.setValidating(false); - try { - DocumentBuilder builder = factory.newDocumentBuilder(); - Document document = builder.parse(is); - NodeList tags = document.getElementsByTagName("classpathentry"); //$NON-NLS-1$ - for (int i = 0, n = tags.getLength(); i < n; i++) { - Element element = (Element) tags.item(i); - String kind = element.getAttribute("kind"); //$NON-NLS-1$ - List<File> addTo = null; - if (kind.equals("src")) { //$NON-NLS-1$ - addTo = sources; - } else if (kind.equals("output")) { //$NON-NLS-1$ - addTo = classes; - } else if (kind.equals("lib")) { //$NON-NLS-1$ - addTo = libraries; - } - if (addTo != null) { - String path = element.getAttribute("path"); //$NON-NLS-1$ - File folder = new File(projectDir, path); - if (folder.exists()) { - addTo.add(folder); - } - } - } - } catch (Exception e) { - log(null, null); - } - } - - // Add in libraries that aren't specified in the .classpath file - File libs = new File(project.getDir(), LIBS_FOLDER); - if (libs.isDirectory()) { - File[] jars = libs.listFiles(); - if (jars != null) { - for (File jar : jars) { - if (LintUtils.endsWith(jar.getPath(), DOT_JAR) - && !libraries.contains(jar)) { - libraries.add(jar); - } - } - } - } - - if (classes.isEmpty()) { - File folder = new File(projectDir, CLASS_FOLDER); - if (folder.exists()) { - classes.add(folder); - } else { - // Maven checks - folder = new File(projectDir, - "target" + File.separator + "classes"); //$NON-NLS-1$ //$NON-NLS-2$ - if (folder.exists()) { - classes.add(folder); - - // If it's maven, also correct the source path, "src" works but - // it's in a more specific subfolder - if (sources.isEmpty()) { - File src = new File(projectDir, - "src" + File.separator //$NON-NLS-1$ - + "main" + File.separator //$NON-NLS-1$ - + "java"); //$NON-NLS-1$ - if (src.exists()) { - sources.add(src); - } else { - src = new File(projectDir, SRC_FOLDER); - if (src.exists()) { - sources.add(src); - } - } - - File gen = new File(projectDir, - "target" + File.separator //$NON-NLS-1$ - + "generated-sources" + File.separator //$NON-NLS-1$ - + "r"); //$NON-NLS-1$ - if (gen.exists()) { - sources.add(gen); - } - } - } - } - } - - // Fallback, in case there is no Eclipse project metadata here - if (sources.isEmpty()) { - File src = new File(projectDir, SRC_FOLDER); - if (src.exists()) { - sources.add(src); - } - File gen = new File(projectDir, GEN_FOLDER); - if (gen.exists()) { - sources.add(gen); - } - } - - info = new ClassPathInfo(sources, classes, libraries); - mProjectInfo.put(project, info); - } - - return info; - } - - /** - * A map from directory to existing projects, or null. Used to ensure that - * projects are unique for a directory (in case we process a library project - * before its including project for example) - */ - private Map<File, Project> mDirToProject; - - /** - * Returns a project for the given directory. This should return the same - * project for the same directory if called repeatedly. - * - * @param dir the directory containing the project - * @param referenceDir See {@link Project#getReferenceDir()}. - * @return a project, never null - */ - @NonNull - public Project getProject(@NonNull File dir, @NonNull File referenceDir) { - if (mDirToProject == null) { - mDirToProject = new HashMap<File, Project>(); - } - - File canonicalDir = dir; - try { - // Attempt to use the canonical handle for the file, in case there - // are symlinks etc present (since when handling library projects, - // we also call getCanonicalFile to compute the result of appending - // relative paths, which can then resolve symlinks and end up with - // a different prefix) - canonicalDir = dir.getCanonicalFile(); - } catch (IOException ioe) { - // pass - } - - Project project = mDirToProject.get(canonicalDir); - if (project != null) { - return project; - } - - project = createProject(dir, referenceDir); - mDirToProject.put(canonicalDir, project); - return project; - } - - private Set<File> mProjectDirs = Sets.newHashSet(); - - /** - * Create a project for the given directory - * @param dir the root directory of the project - * @param referenceDir See {@link Project#getReferenceDir()}. - * @return a new project - */ - @NonNull - protected Project createProject(@NonNull File dir, @NonNull File referenceDir) { - if (mProjectDirs.contains(dir)) { - throw new CircularDependencyException( - "Circular library dependencies; check your project.properties files carefully"); - } - mProjectDirs.add(dir); - return Project.create(this, dir, referenceDir); - } - - /** - * Returns the name of the given project - * - * @param project the project to look up - * @return the name of the project - */ - @NonNull - public String getProjectName(@NonNull Project project) { - return project.getDir().getName(); - } - - private IAndroidTarget[] mTargets; - - /** - * Returns all the {@link IAndroidTarget} versions installed in the user's SDK install - * area. - * - * @return all the installed targets - */ - @NonNull - public IAndroidTarget[] getTargets() { - if (mTargets == null) { - File sdkHome = getSdkHome(); - if (sdkHome != null) { - StdLogger log = new StdLogger(Level.WARNING); - SdkManager manager = SdkManager.createManager(sdkHome.getPath(), log); - mTargets = manager.getTargets(); - } else { - mTargets = new IAndroidTarget[0]; - } - } - - return mTargets; - } - - /** - * Returns the highest known API level. - * - * @return the highest known API level - */ - public int getHighestKnownApiLevel() { - int max = SdkConstants.HIGHEST_KNOWN_API; - - for (IAndroidTarget target : getTargets()) { - if (target.isPlatform()) { - int api = target.getVersion().getApiLevel(); - if (api > max && !target.getVersion().isPreview()) { - max = api; - } - } - } - - return max; - } - - /** - * Returns the super class for the given class name, which should be in VM - * format (e.g. java/lang/Integer, not java.lang.Integer, and using $ rather - * than . for inner classes). If the super class is not known, returns null. - * <p> - * This is typically not necessary, since lint analyzes all the available - * classes. However, if this lint client is invoking lint in an incremental - * context (for example, an IDE offering incremental analysis of a single - * source file), then lint may not see all the classes, and the client can - * provide its own super class lookup. - * - * @param project the project containing the class - * @param name the fully qualified class name - * @return the corresponding super class name (in VM format), or null if not - * known - */ - @Nullable - public String getSuperClass(@NonNull Project project, @NonNull String name) { - return null; - } - - /** - * Checks whether the given name is a subclass of the given super class. If - * the method does not know, it should return null, and otherwise return - * {@link Boolean#TRUE} or {@link Boolean#FALSE}. - * <p> - * Note that the class names are in internal VM format (java/lang/Integer, - * not java.lang.Integer, and using $ rather than . for inner classes). - * - * @param project the project context to look up the class in - * @param name the name of the class to be checked - * @param superClassName the name of the super class to compare to - * @return true if the class of the given name extends the given super class - */ - @Nullable - public Boolean isSubclassOf( - @NonNull Project project, - @NonNull String name, @NonNull - String superClassName) { - return null; - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintDriver.java deleted file mode 100644 index daf2a07..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintDriver.java +++ /dev/null @@ -1,2227 +0,0 @@ -/* - * 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.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.ATTR_IGNORE; -import static com.android.SdkConstants.DOT_CLASS; -import static com.android.SdkConstants.DOT_JAR; -import static com.android.SdkConstants.DOT_JAVA; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.FN_PROJECT_PROGUARD_FILE; -import static com.android.SdkConstants.OLD_PROGUARD_FILE; -import static com.android.SdkConstants.RES_FOLDER; -import static com.android.SdkConstants.SUPPRESS_ALL; -import static com.android.SdkConstants.SUPPRESS_LINT; -import static com.android.SdkConstants.TOOLS_URI; -import static org.objectweb.asm.Opcodes.ASM4; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.annotations.VisibleForTesting; -import com.android.resources.ResourceFolderType; -import com.android.sdklib.IAndroidTarget; -import com.android.tools.lint.client.api.LintListener.EventType; -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.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Project; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 com.google.common.annotations.Beta; -import com.google.common.base.CharMatcher; -import com.google.common.base.Splitter; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.io.ByteStreams; -import com.google.common.io.Closeables; - -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.AnnotationNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.MethodNode; -import org.w3c.dom.Attr; -import org.w3c.dom.Element; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Deque; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import lombok.ast.Annotation; -import lombok.ast.AnnotationElement; -import lombok.ast.AnnotationValue; -import lombok.ast.ArrayInitializer; -import lombok.ast.ClassDeclaration; -import lombok.ast.ConstructorDeclaration; -import lombok.ast.Expression; -import lombok.ast.MethodDeclaration; -import lombok.ast.Modifiers; -import lombok.ast.Node; -import lombok.ast.StrictListAccessor; -import lombok.ast.StringLiteral; -import lombok.ast.TypeReference; -import lombok.ast.VariableDefinition; - -/** - * Analyzes Android projects and files - * <p> - * <b>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.</b> - */ -@Beta -public class LintDriver { - /** - * Max number of passes to run through the lint runner if requested by - * {@link #requestRepeat} - */ - private static final int MAX_PHASES = 3; - private static final String SUPPRESS_LINT_VMSIG = '/' + SUPPRESS_LINT + ';'; - - private final LintClient mClient; - private final IssueRegistry mRegistry; - private volatile boolean mCanceled; - private EnumSet<Scope> mScope; - private List<? extends Detector> mApplicableDetectors; - private Map<Scope, List<Detector>> mScopeDetectors; - private List<LintListener> mListeners; - private int mPhase; - private List<Detector> mRepeatingDetectors; - private EnumSet<Scope> mRepeatScope; - private Project[] mCurrentProjects; - private Project mCurrentProject; - private boolean mAbbreviating = true; - private boolean mParserErrors; - private Map<Object,Object> mProperties; - - /** - * Creates a new {@link LintDriver} - * - * @param registry The registry containing issues to be checked - * @param client the tool wrapping the analyzer, such as an IDE or a CLI - */ - public LintDriver(@NonNull IssueRegistry registry, @NonNull LintClient client) { - mRegistry = registry; - mClient = new LintClientWrapper(client); - } - - /** Cancels the current lint run as soon as possible */ - public void cancel() { - mCanceled = true; - } - - /** - * Returns the scope for the lint job - * - * @return the scope, never null - */ - @NonNull - public EnumSet<Scope> getScope() { - return mScope; - } - - /** - * Returns the lint client requesting the lint check - * - * @return the client, never null - */ - @NonNull - public LintClient getClient() { - return mClient; - } - - /** - * Records a property for later retrieval by {@link #getProperty(Object)} - * - * @param key the key to associate the value with - * @param value the value, or null to remove a previous binding - */ - public void putProperty(@NonNull Object key, @Nullable Object value) { - if (mProperties == null) { - mProperties = Maps.newHashMap(); - } - if (value == null) { - mProperties.remove(key); - } else { - mProperties.put(key, value); - } - } - - /** - * Returns the property previously stored with the given key, or null - * - * @param key the key - * @return the value or null if not found - */ - @Nullable - public Object getProperty(@NonNull Object key) { - if (mProperties != null) { - return mProperties.get(key); - } - - return null; - } - - /** - * Returns the current phase number. The first pass is numbered 1. Only one pass - * will be performed, unless a {@link Detector} calls {@link #requestRepeat}. - * - * @return the current phase, usually 1 - */ - public int getPhase() { - return mPhase; - } - - /** - * Returns the current {@link IssueRegistry}. - * - * @return the current {@link IssueRegistry} - */ - @NonNull - public IssueRegistry getRegistry() { - return mRegistry; - } - - /** - * Returns the project containing a given file, or null if not found. This searches - * only among the currently checked project and its library projects, not among all - * possible projects being scanned sequentially. - * - * @param file the file to be checked - * @return the corresponding project, or null if not found - */ - @Nullable - public Project findProjectFor(@NonNull File file) { - if (mCurrentProjects != null) { - if (mCurrentProjects.length == 1) { - return mCurrentProjects[0]; - } - String path = file.getPath(); - for (Project project : mCurrentProjects) { - if (path.startsWith(project.getDir().getPath())) { - return project; - } - } - } - - return null; - } - - /** - * Sets whether lint should abbreviate output when appropriate. - * - * @param abbreviating true to abbreviate output, false to include everything - */ - public void setAbbreviating(boolean abbreviating) { - mAbbreviating = abbreviating; - } - - /** - * Returns whether lint should abbreviate output when appropriate. - * - * @return true if lint should abbreviate output, false when including everything - */ - public boolean isAbbreviating() { - return mAbbreviating; - } - - /** - * Returns whether lint has encountered any files with fatal parser errors - * (e.g. broken source code, or even broken parsers) - * <p> - * This is useful for checks that need to make sure they've seen all data in - * order to be conclusive (such as an unused resource check). - * - * @return true if any files were not properly processed because they - * contained parser errors - */ - public boolean hasParserErrors() { - return mParserErrors; - } - - /** - * Sets whether lint has encountered files with fatal parser errors. - * - * @see #hasParserErrors() - * @param hasErrors whether parser errors have been encountered - */ - public void setHasParserErrors(boolean hasErrors) { - mParserErrors = hasErrors; - } - - /** - * Returns the projects being analyzed - * - * @return the projects being analyzed - */ - @NonNull - public List<Project> getProjects() { - if (mCurrentProjects != null) { - return Arrays.asList(mCurrentProjects); - } - return Collections.emptyList(); - } - - /** - * Analyze the given file (which can point to an Android project). Issues found - * are reported to the associated {@link LintClient}. - * - * @param files the files and directories to be analyzed - * @param scope the scope of the analysis; detectors with a wider scope will - * not be run. If null, the scope will be inferred from the files. - */ - public void analyze(@NonNull List<File> files, @Nullable EnumSet<Scope> scope) { - mCanceled = false; - mScope = scope; - - Collection<Project> projects; - try { - projects = computeProjects(files); - } catch (CircularDependencyException e) { - mCurrentProject = e.getProject(); - if (mCurrentProject != null) { - File file = e.getLocation().getFile(); - Context context = new Context(this, mCurrentProject, null, file); - context.report(IssueRegistry.LINT_ERROR, e.getLocation(), e.getMessage(), null); - mCurrentProject = null; - } - return; - } - if (projects.isEmpty()) { - mClient.log(null, "No projects found for %1$s", files.toString()); - return; - } - if (mCanceled) { - return; - } - - if (mScope == null) { - // Infer the scope - mScope = EnumSet.noneOf(Scope.class); - for (Project project : projects) { - List<File> subset = project.getSubset(); - if (subset != null) { - for (File file : subset) { - String name = file.getName(); - if (name.equals(ANDROID_MANIFEST_XML)) { - mScope.add(Scope.MANIFEST); - } else if (name.endsWith(DOT_XML)) { - mScope.add(Scope.RESOURCE_FILE); - } else if (name.equals(RES_FOLDER) - || file.getParent().equals(RES_FOLDER)) { - mScope.add(Scope.ALL_RESOURCE_FILES); - mScope.add(Scope.RESOURCE_FILE); - } else if (name.endsWith(DOT_JAVA)) { - mScope.add(Scope.JAVA_FILE); - } else if (name.endsWith(DOT_CLASS)) { - mScope.add(Scope.CLASS_FILE); - } else if (name.equals(OLD_PROGUARD_FILE) - || name.equals(FN_PROJECT_PROGUARD_FILE)) { - mScope.add(Scope.PROGUARD_FILE); - } - } - } else { - // Specified a full project: just use the full project scope - mScope = Scope.ALL; - break; - } - } - } - - fireEvent(EventType.STARTING, null); - - for (Project project : projects) { - mPhase = 1; - - // The set of available detectors varies between projects - computeDetectors(project); - - if (mApplicableDetectors.isEmpty()) { - // No detectors enabled in this project: skip it - continue; - } - - checkProject(project); - if (mCanceled) { - break; - } - - runExtraPhases(project); - } - - fireEvent(mCanceled ? EventType.CANCELED : EventType.COMPLETED, null); - } - - private void runExtraPhases(Project project) { - // Did any detectors request another phase? - if (mRepeatingDetectors != null) { - // Yes. Iterate up to MAX_PHASES times. - - // During the extra phases, we might be narrowing the scope, and setting it in the - // scope field such that detectors asking about the available scope will get the - // correct result. However, we need to restore it to the original scope when this - // is done in case there are other projects that will be checked after this, since - // the repeated phases is done *per project*, not after all projects have been - // processed. - EnumSet<Scope> oldScope = mScope; - - do { - mPhase++; - fireEvent(EventType.NEW_PHASE, - new Context(this, project, null, project.getDir())); - - // Narrow the scope down to the set of scopes requested by - // the rules. - if (mRepeatScope == null) { - mRepeatScope = Scope.ALL; - } - mScope = Scope.intersect(mScope, mRepeatScope); - if (mScope.isEmpty()) { - break; - } - - // Compute the detectors to use for this pass. - // Unlike the normal computeDetectors(project) call, - // this is going to use the existing instances, and include - // those that apply for the configuration. - computeRepeatingDetectors(mRepeatingDetectors, project); - - if (mApplicableDetectors.isEmpty()) { - // No detectors enabled in this project: skip it - continue; - } - - checkProject(project); - if (mCanceled) { - break; - } - } while (mPhase < MAX_PHASES && mRepeatingDetectors != null); - - mScope = oldScope; - } - } - - private void computeRepeatingDetectors(List<Detector> detectors, Project project) { - // Ensure that the current visitor is recomputed - mCurrentFolderType = null; - mCurrentVisitor = null; - - // Create map from detector class to issue such that we can - // compute applicable issues for each detector in the list of detectors - // to be repeated - List<Issue> issues = mRegistry.getIssues(); - Multimap<Class<? extends Detector>, Issue> issueMap = - ArrayListMultimap.create(issues.size(), 3); - for (Issue issue : issues) { - issueMap.put(issue.getDetectorClass(), issue); - } - - Map<Class<? extends Detector>, EnumSet<Scope>> detectorToScope = - new HashMap<Class<? extends Detector>, EnumSet<Scope>>(); - Map<Scope, List<Detector>> scopeToDetectors = - new EnumMap<Scope, List<Detector>>(Scope.class); - - List<Detector> detectorList = new ArrayList<Detector>(); - // Compute the list of detectors (narrowed down from mRepeatingDetectors), - // and simultaneously build up the detectorToScope map which tracks - // the scopes each detector is affected by (this is used to populate - // the mScopeDetectors map which is used during iteration). - Configuration configuration = project.getConfiguration(); - for (Detector detector : detectors) { - Class<? extends Detector> detectorClass = detector.getClass(); - Collection<Issue> detectorIssues = issueMap.get(detectorClass); - if (detectorIssues != null) { - boolean add = false; - for (Issue issue : detectorIssues) { - // The reason we have to check whether the detector is enabled - // is that this is a per-project property, so when running lint in multiple - // projects, a detector enabled only in a different project could have - // requested another phase, and we end up in this project checking whether - // the detector is enabled here. - if (!configuration.isEnabled(issue)) { - continue; - } - - add = true; // Include detector if any of its issues are enabled - - EnumSet<Scope> s = detectorToScope.get(detectorClass); - EnumSet<Scope> issueScope = issue.getScope(); - if (s == null) { - detectorToScope.put(detectorClass, issueScope); - } else if (!s.containsAll(issueScope)) { - EnumSet<Scope> union = EnumSet.copyOf(s); - union.addAll(issueScope); - detectorToScope.put(detectorClass, union); - } - } - - if (add) { - detectorList.add(detector); - EnumSet<Scope> union = detectorToScope.get(detector.getClass()); - for (Scope s : union) { - List<Detector> list = scopeToDetectors.get(s); - if (list == null) { - list = new ArrayList<Detector>(); - scopeToDetectors.put(s, list); - } - list.add(detector); - } - } - } - } - - mApplicableDetectors = detectorList; - mScopeDetectors = scopeToDetectors; - mRepeatingDetectors = null; - mRepeatScope = null; - - validateScopeList(); - } - - private void computeDetectors(@NonNull Project project) { - // Ensure that the current visitor is recomputed - mCurrentFolderType = null; - mCurrentVisitor = null; - - Configuration configuration = project.getConfiguration(); - mScopeDetectors = new EnumMap<Scope, List<Detector>>(Scope.class); - mApplicableDetectors = mRegistry.createDetectors(mClient, configuration, - mScope, mScopeDetectors); - - validateScopeList(); - } - - /** Development diagnostics only, run with assertions on */ - @SuppressWarnings("all") // Turn off warnings for the intentional assertion side effect below - private void validateScopeList() { - boolean assertionsEnabled = false; - assert assertionsEnabled = true; // Intentional side-effect - if (assertionsEnabled) { - List<Detector> resourceFileDetectors = mScopeDetectors.get(Scope.RESOURCE_FILE); - if (resourceFileDetectors != null) { - for (Detector detector : resourceFileDetectors) { - assert detector instanceof ResourceXmlDetector : detector; - } - } - - List<Detector> manifestDetectors = mScopeDetectors.get(Scope.MANIFEST); - if (manifestDetectors != null) { - for (Detector detector : manifestDetectors) { - assert detector instanceof Detector.XmlScanner : detector; - } - } - List<Detector> javaCodeDetectors = mScopeDetectors.get(Scope.ALL_JAVA_FILES); - if (javaCodeDetectors != null) { - for (Detector detector : javaCodeDetectors) { - assert detector instanceof Detector.JavaScanner : detector; - } - } - List<Detector> javaFileDetectors = mScopeDetectors.get(Scope.JAVA_FILE); - if (javaFileDetectors != null) { - for (Detector detector : javaFileDetectors) { - assert detector instanceof Detector.JavaScanner : detector; - } - } - - List<Detector> classDetectors = mScopeDetectors.get(Scope.CLASS_FILE); - if (classDetectors != null) { - for (Detector detector : classDetectors) { - assert detector instanceof Detector.ClassScanner : detector; - } - } - - List<Detector> classCodeDetectors = mScopeDetectors.get(Scope.ALL_CLASS_FILES); - if (classCodeDetectors != null) { - for (Detector detector : classCodeDetectors) { - assert detector instanceof Detector.ClassScanner : detector; - } - } - - List<Detector> otherDetectors = mScopeDetectors.get(Scope.OTHER_SCOPE); - if (otherDetectors != null) { - for (Detector detector : otherDetectors) { - assert detector instanceof Detector.OtherFileScanner : detector; - } - } - } - } - - private void registerProjectFile( - @NonNull Map<File, Project> fileToProject, - @NonNull File file, - @NonNull File projectDir, - @NonNull File rootDir) { - fileToProject.put(file, mClient.getProject(projectDir, rootDir)); - } - - private Collection<Project> computeProjects(@NonNull List<File> files) { - // Compute list of projects - Map<File, Project> fileToProject = new HashMap<File, Project>(); - - File sharedRoot = null; - - // Ensure that we have absolute paths such that if you lint - // "foo bar" in "baz" we can show baz/ as the root - if (files.size() > 1) { - List<File> absolute = new ArrayList<File>(files.size()); - for (File file : files) { - absolute.add(file.getAbsoluteFile()); - } - files = absolute; - - sharedRoot = LintUtils.getCommonParent(files); - if (sharedRoot != null && sharedRoot.getParentFile() == null) { // "/" ? - sharedRoot = null; - } - } - - - for (File file : files) { - if (file.isDirectory()) { - File rootDir = sharedRoot; - if (rootDir == null) { - rootDir = file; - if (files.size() > 1) { - rootDir = file.getParentFile(); - if (rootDir == null) { - rootDir = file; - } - } - } - - // Figure out what to do with a directory. Note that the meaning of the - // directory can be ambiguous: - // If you pass a directory which is unknown, we don't know if we should - // search upwards (in case you're pointing at a deep java package folder - // within the project), or if you're pointing at some top level directory - // containing lots of projects you want to scan. We attempt to do the - // right thing, which is to see if you're pointing right at a project or - // right within it (say at the src/ or res/) folder, and if not, you're - // hopefully pointing at a project tree that you want to scan recursively. - if (LintUtils.isProjectDir(file)) { - registerProjectFile(fileToProject, file, file, rootDir); - continue; - } else { - File parent = file.getParentFile(); - if (parent != null) { - if (LintUtils.isProjectDir(parent)) { - registerProjectFile(fileToProject, file, parent, parent); - continue; - } else { - parent = parent.getParentFile(); - if (parent != null && LintUtils.isProjectDir(parent)) { - registerProjectFile(fileToProject, file, parent, parent); - continue; - } - } - } - - // Search downwards for nested projects - addProjects(file, fileToProject, rootDir); - } - } else { - // Pointed at a file: Search upwards for the containing project - File parent = file.getParentFile(); - while (parent != null) { - if (LintUtils.isProjectDir(parent)) { - registerProjectFile(fileToProject, file, parent, parent); - break; - } - parent = parent.getParentFile(); - } - } - - if (mCanceled) { - return Collections.emptySet(); - } - } - - for (Map.Entry<File, Project> entry : fileToProject.entrySet()) { - File file = entry.getKey(); - Project project = entry.getValue(); - if (!file.equals(project.getDir())) { - if (file.isDirectory()) { - try { - File dir = file.getCanonicalFile(); - if (dir.equals(project.getDir())) { - continue; - } - } catch (IOException ioe) { - // pass - } - } - - project.addFile(file); - } - } - - // Partition the projects up such that we only return projects that aren't - // included by other projects (e.g. because they are library projects) - - Collection<Project> allProjects = fileToProject.values(); - Set<Project> roots = new HashSet<Project>(allProjects); - for (Project project : allProjects) { - roots.removeAll(project.getAllLibraries()); - } - - // Report issues for all projects that are explicitly referenced. We need to - // do this here, since the project initialization will mark all library - // projects as no-report projects by default. - for (Project project : allProjects) { - // Report issues for all projects explicitly listed or found via a directory - // traversal -- including library projects. - project.setReportIssues(true); - } - - if (LintUtils.assertionsEnabled()) { - // Make sure that all the project directories are unique. This ensures - // that we didn't accidentally end up with different project instances - // for a library project discovered as a directory as well as one - // initialized from the library project dependency list - IdentityHashMap<Project, Project> projects = - new IdentityHashMap<Project, Project>(); - for (Project project : roots) { - projects.put(project, project); - for (Project library : project.getAllLibraries()) { - projects.put(library, library); - } - } - Set<File> dirs = new HashSet<File>(); - for (Project project : projects.keySet()) { - assert !dirs.contains(project.getDir()); - dirs.add(project.getDir()); - } - } - - return roots; - } - - private void addProjects( - @NonNull File dir, - @NonNull Map<File, Project> fileToProject, - @NonNull File rootDir) { - if (mCanceled) { - return; - } - - if (LintUtils.isProjectDir(dir)) { - registerProjectFile(fileToProject, dir, dir, rootDir); - } else { - File[] files = dir.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - addProjects(file, fileToProject, rootDir); - } - } - } - } - } - - private void checkProject(@NonNull Project project) { - File projectDir = project.getDir(); - - Context projectContext = new Context(this, project, null, projectDir); - fireEvent(EventType.SCANNING_PROJECT, projectContext); - - List<Project> allLibraries = project.getAllLibraries(); - Set<Project> allProjects = new HashSet<Project>(allLibraries.size() + 1); - allProjects.add(project); - allProjects.addAll(allLibraries); - mCurrentProjects = allProjects.toArray(new Project[allProjects.size()]); - - mCurrentProject = project; - - for (Detector check : mApplicableDetectors) { - check.beforeCheckProject(projectContext); - if (mCanceled) { - return; - } - } - - assert mCurrentProject == project; - runFileDetectors(project, project); - - if (!Scope.checkSingleFile(mScope)) { - List<Project> libraries = project.getDirectLibraries(); - for (Project library : libraries) { - Context libraryContext = new Context(this, library, project, projectDir); - fireEvent(EventType.SCANNING_LIBRARY_PROJECT, libraryContext); - mCurrentProject = library; - - for (Detector check : mApplicableDetectors) { - check.beforeCheckLibraryProject(libraryContext); - if (mCanceled) { - return; - } - } - assert mCurrentProject == library; - - runFileDetectors(library, project); - if (mCanceled) { - return; - } - - assert mCurrentProject == library; - - for (Detector check : mApplicableDetectors) { - check.afterCheckLibraryProject(libraryContext); - if (mCanceled) { - return; - } - } - } - } - - mCurrentProject = project; - - for (Detector check : mApplicableDetectors) { - check.afterCheckProject(projectContext); - if (mCanceled) { - return; - } - } - - if (mCanceled) { - mClient.report( - projectContext, - // Must provide an issue since API guarantees that the issue parameter - // is valid - Issue.create("Lint", "", "", Category.PERFORMANCE, 0, Severity.INFORMATIONAL, //$NON-NLS-1$ - Detector.class, EnumSet.noneOf(Scope.class)), - Severity.INFORMATIONAL, - null /*range*/, - "Lint canceled by user", null); - } - - mCurrentProjects = null; - } - - private void runFileDetectors(@NonNull Project project, @Nullable Project main) { - // Look up manifest information (but not for library projects) - File manifestFile = project.getManifestFile(); - if (manifestFile != null) { - XmlContext context = new XmlContext(this, project, main, manifestFile, null); - IDomParser parser = mClient.getDomParser(); - if (parser != null) { - context.document = parser.parseXml(context); - if (context.document != null) { - try { - project.readManifest(context.document); - - if ((!project.isLibrary() || (main != null && main.isMergingManifests())) - && mScope.contains(Scope.MANIFEST)) { - List<Detector> detectors = mScopeDetectors.get(Scope.MANIFEST); - if (detectors != null) { - XmlVisitor v = new XmlVisitor(parser, detectors); - fireEvent(EventType.SCANNING_FILE, context); - v.visitFile(context, manifestFile); - } - } - } finally { - if (context.document != null) { // else: freed by XmlVisitor above - parser.dispose(context, context.document); - } - } - } - } - } - - // Process both Scope.RESOURCE_FILE and Scope.ALL_RESOURCE_FILES detectors together - // in a single pass through the resource directories. - if (mScope.contains(Scope.ALL_RESOURCE_FILES) || mScope.contains(Scope.RESOURCE_FILE)) { - List<Detector> checks = union(mScopeDetectors.get(Scope.RESOURCE_FILE), - mScopeDetectors.get(Scope.ALL_RESOURCE_FILES)); - if (checks != null && !checks.isEmpty()) { - List<ResourceXmlDetector> xmlDetectors = - new ArrayList<ResourceXmlDetector>(checks.size()); - for (Detector detector : checks) { - if (detector instanceof ResourceXmlDetector) { - xmlDetectors.add((ResourceXmlDetector) detector); - } - } - if (!xmlDetectors.isEmpty()) { - List<File> files = project.getSubset(); - if (files != null) { - checkIndividualResources(project, main, xmlDetectors, files); - } else { - List<File> resourceFolders = project.getResourceFolders(); - if (!resourceFolders.isEmpty() && !xmlDetectors.isEmpty()) { - for (File res : resourceFolders) { - checkResFolder(project, main, res, xmlDetectors); - } - } - } - } - } - } - - if (mCanceled) { - return; - } - - if (mScope.contains(Scope.JAVA_FILE) || mScope.contains(Scope.ALL_JAVA_FILES)) { - List<Detector> checks = union(mScopeDetectors.get(Scope.JAVA_FILE), - mScopeDetectors.get(Scope.ALL_JAVA_FILES)); - if (checks != null && !checks.isEmpty()) { - List<File> files = project.getSubset(); - if (files != null) { - checkIndividualJavaFiles(project, main, checks, files); - } else { - List<File> sourceFolders = project.getJavaSourceFolders(); - checkJava(project, main, sourceFolders, checks); - } - } - } - - if (mCanceled) { - return; - } - - if (mScope.contains(Scope.CLASS_FILE) - || mScope.contains(Scope.ALL_CLASS_FILES) - || mScope.contains(Scope.JAVA_LIBRARIES)) { - checkClasses(project, main); - } - - if (mScope.contains(Scope.OTHER)) { - List<Detector> checks = mScopeDetectors.get(Scope.OTHER); - if (checks != null) { - OtherFileVisitor visitor = new OtherFileVisitor(checks); - visitor.scan(this, project, main); - } - } - - if (mCanceled) { - return; - } - - if (project == main && mScope.contains(Scope.PROGUARD_FILE)) { - checkProGuard(project, main); - } - } - - private void checkProGuard(Project project, Project main) { - List<Detector> detectors = mScopeDetectors.get(Scope.PROGUARD_FILE); - if (detectors != null) { - Project p = main != null ? main : project; - List<File> files = new ArrayList<File>(); - String paths = p.getProguardPath(); - if (paths != null) { - Splitter splitter = Splitter.on(CharMatcher.anyOf(":;")); //$NON-NLS-1$ - for (String path : splitter.split(paths)) { - if (path.contains("${")) { //$NON-NLS-1$ - // Don't analyze the global/user proguard files - continue; - } - File file = new File(path); - if (!file.isAbsolute()) { - file = new File(project.getDir(), path); - } - if (file.exists()) { - files.add(file); - } - } - } - if (files.isEmpty()) { - File file = new File(project.getDir(), OLD_PROGUARD_FILE); - if (file.exists()) { - files.add(file); - } - file = new File(project.getDir(), FN_PROJECT_PROGUARD_FILE); - if (file.exists()) { - files.add(file); - } - } - for (File file : files) { - Context context = new Context(this, project, main, file); - fireEvent(EventType.SCANNING_FILE, context); - for (Detector detector : detectors) { - if (detector.appliesTo(context, file)) { - detector.beforeCheckFile(context); - detector.run(context); - detector.afterCheckFile(context); - } - } - } - } - } - - /** True if execution has been canceled */ - boolean isCanceled() { - return mCanceled; - } - - /** - * Map from VM class name to corresponding super class VM name, if available. - * This map is typically null except <b>during</b> class processing. - */ - private Map<String, String> mSuperClassMap; - - /** - * Returns the super class for the given class name, - * which should be in VM format (e.g. java/lang/Integer, not java.lang.Integer). - * If the super class is not known, returns null. This can happen if - * the given class is not a known class according to the project or its - * libraries, for example because it refers to one of the core libraries which - * are not analyzed by lint. - * - * @param name the fully qualified class name - * @return the corresponding super class name (in VM format), or null if not known - */ - @Nullable - public String getSuperClass(@NonNull String name) { - if (mSuperClassMap == null) { - throw new IllegalStateException("Only callable during ClassScanner#checkClass"); - } - assert name.indexOf('.') == -1 : "Use VM signatures, e.g. java/lang/Integer"; - - String superClass = mSuperClassMap.get(name); - if (superClass == null && mCurrentProject != null) { - if ("java/lang/Object".equals(name)) { //$NON-NLS-1$ - return null; - } - superClass = mClient.getSuperClass(mCurrentProject, name); - if (superClass != null) { - mSuperClassMap.put(name, superClass); - } - } - - return superClass; - } - - /** - * Returns true if the given class is a subclass of the given super class. - * - * @param classNode the class to check whether it is a subclass of the given - * super class name - * @param superClassName the fully qualified super class name (in VM format, - * e.g. java/lang/Integer, not java.lang.Integer. - * @return true if the given class is a subclass of the given super class - */ - public boolean isSubclassOf(@NonNull ClassNode classNode, @NonNull String superClassName) { - if (superClassName.equals(classNode.superName)) { - return true; - } - - if (mCurrentProject != null) { - Boolean isSub = mClient.isSubclassOf(mCurrentProject, classNode.name, superClassName); - if (isSub != null) { - return isSub.booleanValue(); - } - } - - String className = classNode.name; - while (className != null) { - if (className.equals(superClassName)) { - return true; - } - className = getSuperClass(className); - } - - return false; - } - @Nullable - private static List<Detector> union( - @Nullable List<Detector> list1, - @Nullable List<Detector> list2) { - if (list1 == null) { - return list2; - } else if (list2 == null) { - return list1; - } else { - // Use set to pick out unique detectors, since it's possible for there to be overlap, - // e.g. the DuplicateIdDetector registers both a cross-resource issue and a - // single-file issue, so it shows up on both scope lists: - Set<Detector> set = new HashSet<Detector>(list1.size() + list2.size()); - if (list1 != null) { - set.addAll(list1); - } - if (list2 != null) { - set.addAll(list2); - } - - return new ArrayList<Detector>(set); - } - } - - /** Check the classes in this project (and if applicable, in any library projects */ - private void checkClasses(Project project, Project main) { - List<File> files = project.getSubset(); - if (files != null) { - checkIndividualClassFiles(project, main, files); - return; - } - - // We need to read in all the classes up front such that we can initialize - // the parent chains (such that for example for a virtual dispatch, we can - // also check the super classes). - - List<File> libraries = project.getJavaLibraries(); - List<ClassEntry> libraryEntries; - if (!libraries.isEmpty()) { - libraryEntries = new ArrayList<ClassEntry>(64); - findClasses(libraryEntries, libraries); - Collections.sort(libraryEntries); - } else { - libraryEntries = Collections.emptyList(); - } - - List<File> classFolders = project.getJavaClassFolders(); - List<ClassEntry> classEntries; - if (classFolders.isEmpty()) { - String message = String.format("No .class files were found in project \"%1$s\", " - + "so none of the classfile based checks could be run. " - + "Does the project need to be built first?", project.getName()); - Location location = Location.create(project.getDir()); - mClient.report(new Context(this, project, main, project.getDir()), - IssueRegistry.LINT_ERROR, - project.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR), - location, message, null); - classEntries = Collections.emptyList(); - } else { - classEntries = new ArrayList<ClassEntry>(64); - findClasses(classEntries, classFolders); - Collections.sort(classEntries); - } - - if (getPhase() == 1) { - mSuperClassMap = getSuperMap(libraryEntries, classEntries); - } - - // Actually run the detectors. Libraries should be called before the - // main classes. - runClassDetectors(Scope.JAVA_LIBRARIES, libraryEntries, project, main); - - if (mCanceled) { - return; - } - - runClassDetectors(Scope.CLASS_FILE, classEntries, project, main); - runClassDetectors(Scope.ALL_CLASS_FILES, classEntries, project, main); - } - - private void checkIndividualClassFiles( - @NonNull Project project, - @Nullable Project main, - @NonNull List<File> files) { - List<ClassEntry> entries = new ArrayList<ClassEntry>(files.size()); - - List<File> classFolders = project.getJavaClassFolders(); - if (!classFolders.isEmpty()) { - for (File file : files) { - String path = file.getPath(); - if (file.isFile() && path.endsWith(DOT_CLASS)) { - try { - byte[] bytes = mClient.readBytes(file); - if (bytes != null) { - for (File dir : classFolders) { - if (path.startsWith(dir.getPath())) { - entries.add(new ClassEntry(file, null /* jarFile*/, dir, - bytes)); - break; - } - } - } - } catch (IOException e) { - mClient.log(e, null); - continue; - } - - if (mCanceled) { - return; - } - } - } - - if (!entries.isEmpty()) { - Collections.sort(entries); - // No superclass info available on individual lint runs, unless - // the client can provide it - mSuperClassMap = Maps.newHashMap(); - runClassDetectors(Scope.CLASS_FILE, entries, project, main); - } - } - } - - /** - * Stack of {@link ClassNode} nodes for outer classes of the currently - * processed class, including that class itself. Populated by - * {@link #runClassDetectors(Scope, List, Project, Project)} and used by - * {@link #getOuterClassNode(ClassNode)} - */ - private Deque<ClassNode> mOuterClasses; - - private void runClassDetectors(Scope scope, List<ClassEntry> entries, - Project project, Project main) { - if (mScope.contains(scope)) { - List<Detector> classDetectors = mScopeDetectors.get(scope); - if (classDetectors != null && !classDetectors.isEmpty() && !entries.isEmpty()) { - AsmVisitor visitor = new AsmVisitor(mClient, classDetectors); - - String sourceContents = null; - String sourceName = ""; - mOuterClasses = new ArrayDeque<ClassNode>(); - for (ClassEntry entry : entries) { - ClassReader reader; - ClassNode classNode; - try { - reader = new ClassReader(entry.bytes); - classNode = new ClassNode(); - reader.accept(classNode, 0 /* flags */); - } catch (Throwable t) { - mClient.log(null, "Error processing %1$s: broken class file?", - entry.path()); - continue; - } - - ClassNode peek; - while ((peek = mOuterClasses.peek()) != null) { - if (classNode.name.startsWith(peek.name)) { - break; - } else { - mOuterClasses.pop(); - } - } - mOuterClasses.push(classNode); - - if (isSuppressed(null, classNode)) { - // Class was annotated with suppress all -- no need to look any further - continue; - } - - if (sourceContents != null) { - // Attempt to reuse the source buffer if initialized - // This means making sure that the source files - // foo/bar/MyClass and foo/bar/MyClass$Bar - // and foo/bar/MyClass$3 and foo/bar/MyClass$3$1 have the same prefix. - String newName = classNode.name; - int newRootLength = newName.indexOf('$'); - if (newRootLength == -1) { - newRootLength = newName.length(); - } - int oldRootLength = sourceName.indexOf('$'); - if (oldRootLength == -1) { - oldRootLength = sourceName.length(); - } - if (newRootLength != oldRootLength || - !sourceName.regionMatches(0, newName, 0, newRootLength)) { - sourceContents = null; - } - } - - ClassContext context = new ClassContext(this, project, main, - entry.file, entry.jarFile, entry.binDir, entry.bytes, - classNode, scope == Scope.JAVA_LIBRARIES /*fromLibrary*/, - sourceContents); - - try { - visitor.runClassDetectors(context); - } catch (Exception e) { - mClient.log(e, null); - } - - if (mCanceled) { - return; - } - - sourceContents = context.getSourceContents(false/*read*/); - sourceName = classNode.name; - } - - mOuterClasses = null; - } - } - } - - /** Returns the outer class node of the given class node - * @param classNode the inner class node - * @return the outer class node */ - public ClassNode getOuterClassNode(@NonNull ClassNode classNode) { - String outerName = classNode.outerClass; - - Iterator<ClassNode> iterator = mOuterClasses.iterator(); - while (iterator.hasNext()) { - ClassNode node = iterator.next(); - if (outerName != null) { - if (node.name.equals(outerName)) { - return node; - } - } else if (node == classNode) { - return iterator.hasNext() ? iterator.next() : null; - } - } - - return null; - } - - private Map<String, String> getSuperMap(List<ClassEntry> libraryEntries, - List<ClassEntry> classEntries) { - int size = libraryEntries.size() + classEntries.size(); - Map<String, String> map = new HashMap<String, String>(size); - - SuperclassVisitor visitor = new SuperclassVisitor(map); - addSuperClasses(visitor, libraryEntries); - addSuperClasses(visitor, classEntries); - - return map; - } - - private void addSuperClasses(SuperclassVisitor visitor, List<ClassEntry> entries) { - for (ClassEntry entry : entries) { - try { - ClassReader reader = new ClassReader(entry.bytes); - int flags = ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG - | ClassReader.SKIP_FRAMES; - reader.accept(visitor, flags); - } catch (Throwable t) { - mClient.log(null, "Error processing %1$s: broken class file?", entry.path()); - } - } - } - - /** Visitor skimming classes and initializing a map of super classes */ - private static class SuperclassVisitor extends ClassVisitor { - private final Map<String, String> mMap; - - public SuperclassVisitor(Map<String, String> map) { - super(ASM4); - mMap = map; - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, - String[] interfaces) { - if (superName != null) { - mMap.put(name, superName); - } - } - } - - private void findClasses( - @NonNull List<ClassEntry> entries, - @NonNull List<File> classPath) { - for (File classPathEntry : classPath) { - if (classPathEntry.getName().endsWith(DOT_JAR)) { - File jarFile = classPathEntry; - if (!jarFile.exists()) { - continue; - } - ZipInputStream zis = null; - try { - FileInputStream fis = new FileInputStream(jarFile); - zis = new ZipInputStream(fis); - ZipEntry entry = zis.getNextEntry(); - while (entry != null) { - String name = entry.getName(); - if (name.endsWith(DOT_CLASS)) { - try { - byte[] bytes = ByteStreams.toByteArray(zis); - if (bytes != null) { - File file = new File(entry.getName()); - entries.add(new ClassEntry(file, jarFile, jarFile, bytes)); - } - } catch (Exception e) { - mClient.log(e, null); - continue; - } - } - - if (mCanceled) { - return; - } - - entry = zis.getNextEntry(); - } - } catch (IOException e) { - mClient.log(e, "Could not read jar file contents from %1$s", jarFile); - } finally { - Closeables.closeQuietly(zis); - } - - continue; - } else if (classPathEntry.isDirectory()) { - File binDir = classPathEntry; - List<File> classFiles = new ArrayList<File>(); - addClassFiles(binDir, classFiles); - - for (File file : classFiles) { - try { - byte[] bytes = mClient.readBytes(file); - if (bytes != null) { - entries.add(new ClassEntry(file, null /* jarFile*/, binDir, bytes)); - } - } catch (IOException e) { - mClient.log(e, null); - continue; - } - - if (mCanceled) { - return; - } - } - } else { - mClient.log(null, "Ignoring class path entry %1$s", classPathEntry); - } - } - } - - private static void addClassFiles(@NonNull File dir, @NonNull List<File> classFiles) { - // Process the resource folder - File[] files = dir.listFiles(); - if (files != null && files.length > 0) { - for (File file : files) { - if (file.isFile() && file.getName().endsWith(DOT_CLASS)) { - classFiles.add(file); - } else if (file.isDirectory()) { - // Recurse - addClassFiles(file, classFiles); - } - } - } - } - - private void checkJava( - @NonNull Project project, - @Nullable Project main, - @NonNull List<File> sourceFolders, - @NonNull List<Detector> checks) { - IJavaParser javaParser = mClient.getJavaParser(); - if (javaParser == null) { - mClient.log(null, "No java parser provided to lint: not running Java checks"); - return; - } - - assert !checks.isEmpty(); - - // Gather all Java source files in a single pass; more efficient. - List<File> sources = new ArrayList<File>(100); - for (File folder : sourceFolders) { - gatherJavaFiles(folder, sources); - } - if (!sources.isEmpty()) { - JavaVisitor visitor = new JavaVisitor(javaParser, checks); - for (File file : sources) { - JavaContext context = new JavaContext(this, project, main, file); - fireEvent(EventType.SCANNING_FILE, context); - visitor.visitFile(context, file); - if (mCanceled) { - return; - } - } - } - } - - private void checkIndividualJavaFiles( - @NonNull Project project, - @Nullable Project main, - @NonNull List<Detector> checks, - @NonNull List<File> files) { - - IJavaParser javaParser = mClient.getJavaParser(); - if (javaParser == null) { - mClient.log(null, "No java parser provided to lint: not running Java checks"); - return; - } - - JavaVisitor visitor = new JavaVisitor(javaParser, checks); - - for (File file : files) { - if (file.isFile() && file.getPath().endsWith(DOT_JAVA)) { - JavaContext context = new JavaContext(this, project, main, file); - fireEvent(EventType.SCANNING_FILE, context); - visitor.visitFile(context, file); - if (mCanceled) { - return; - } - } - } - } - - private static void gatherJavaFiles(@NonNull File dir, @NonNull List<File> result) { - File[] files = dir.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isFile() && file.getName().endsWith(".java")) { //$NON-NLS-1$ - result.add(file); - } else if (file.isDirectory()) { - gatherJavaFiles(file, result); - } - } - } - } - - private ResourceFolderType mCurrentFolderType; - private List<ResourceXmlDetector> mCurrentXmlDetectors; - private XmlVisitor mCurrentVisitor; - - @Nullable - private XmlVisitor getVisitor( - @NonNull ResourceFolderType type, - @NonNull List<ResourceXmlDetector> checks) { - if (type != mCurrentFolderType) { - mCurrentFolderType = type; - - // Determine which XML resource detectors apply to the given folder type - List<ResourceXmlDetector> applicableChecks = - new ArrayList<ResourceXmlDetector>(checks.size()); - for (ResourceXmlDetector check : checks) { - if (check.appliesTo(type)) { - applicableChecks.add(check); - } - } - - // If the list of detectors hasn't changed, then just use the current visitor! - if (mCurrentXmlDetectors != null && mCurrentXmlDetectors.equals(applicableChecks)) { - return mCurrentVisitor; - } - - if (applicableChecks.isEmpty()) { - mCurrentVisitor = null; - return null; - } - - IDomParser parser = mClient.getDomParser(); - if (parser != null) { - mCurrentVisitor = new XmlVisitor(parser, applicableChecks); - } - } - - return mCurrentVisitor; - } - - private void checkResFolder( - @NonNull Project project, - @Nullable Project main, - @NonNull File res, - @NonNull List<ResourceXmlDetector> checks) { - assert res.isDirectory(); - File[] resourceDirs = res.listFiles(); - if (resourceDirs == null) { - return; - } - - // Sort alphabetically such that we can process related folder types at the - // same time - - Arrays.sort(resourceDirs); - for (File dir : resourceDirs) { - ResourceFolderType type = ResourceFolderType.getFolderType(dir.getName()); - if (type != null) { - checkResourceFolder(project, main, dir, type, checks); - } - - if (mCanceled) { - return; - } - } - } - - private void checkResourceFolder( - @NonNull Project project, - @Nullable Project main, - @NonNull File dir, - @NonNull ResourceFolderType type, - @NonNull List<ResourceXmlDetector> checks) { - // Process the resource folder - File[] xmlFiles = dir.listFiles(); - if (xmlFiles != null && xmlFiles.length > 0) { - XmlVisitor visitor = getVisitor(type, checks); - if (visitor != null) { // if not, there are no applicable rules in this folder - for (File file : xmlFiles) { - if (LintUtils.isXmlFile(file)) { - XmlContext context = new XmlContext(this, project, main, file, type); - fireEvent(EventType.SCANNING_FILE, context); - visitor.visitFile(context, file); - if (mCanceled) { - return; - } - } - } - } - } - } - - /** Checks individual resources */ - private void checkIndividualResources( - @NonNull Project project, - @Nullable Project main, - @NonNull List<ResourceXmlDetector> xmlDetectors, - @NonNull List<File> files) { - for (File file : files) { - if (file.isDirectory()) { - // Is it a resource folder? - ResourceFolderType type = ResourceFolderType.getFolderType(file.getName()); - if (type != null && new File(file.getParentFile(), RES_FOLDER).exists()) { - // Yes. - checkResourceFolder(project, main, file, type, xmlDetectors); - } else if (file.getName().equals(RES_FOLDER)) { // Is it the res folder? - // Yes - checkResFolder(project, main, file, xmlDetectors); - } else { - mClient.log(null, "Unexpected folder %1$s; should be project, " + - "\"res\" folder or resource folder", file.getPath()); - continue; - } - } else if (file.isFile() && LintUtils.isXmlFile(file)) { - // Yes, find out its resource type - String folderName = file.getParentFile().getName(); - ResourceFolderType type = ResourceFolderType.getFolderType(folderName); - if (type != null) { - XmlVisitor visitor = getVisitor(type, xmlDetectors); - if (visitor != null) { - XmlContext context = new XmlContext(this, project, main, file, type); - fireEvent(EventType.SCANNING_FILE, context); - visitor.visitFile(context, file); - } - } - } - } - } - - /** - * Adds a listener to be notified of lint progress - * - * @param listener the listener to be added - */ - public void addLintListener(@NonNull LintListener listener) { - if (mListeners == null) { - mListeners = new ArrayList<LintListener>(1); - } - mListeners.add(listener); - } - - /** - * Removes a listener such that it is no longer notified of progress - * - * @param listener the listener to be removed - */ - public void removeLintListener(@NonNull LintListener listener) { - mListeners.remove(listener); - if (mListeners.isEmpty()) { - mListeners = null; - } - } - - /** Notifies listeners, if any, that the given event has occurred */ - private void fireEvent(@NonNull LintListener.EventType type, @Nullable Context context) { - if (mListeners != null) { - for (LintListener listener : mListeners) { - listener.update(this, type, context); - } - } - } - - /** - * Wrapper around the lint client. This sits in the middle between a - * detector calling for example {@link LintClient#report} and - * the actual embedding tool, and performs filtering etc such that detectors - * and lint clients don't have to make sure they check for ignored issues or - * filtered out warnings. - */ - private class LintClientWrapper extends LintClient { - @NonNull - private final LintClient mDelegate; - - public LintClientWrapper(@NonNull LintClient delegate) { - mDelegate = delegate; - } - - @Override - public void report( - @NonNull Context context, - @NonNull Issue issue, - @NonNull Severity severity, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - assert mCurrentProject != null; - if (!mCurrentProject.getReportIssues()) { - return; - } - - Configuration configuration = context.getConfiguration(); - if (!configuration.isEnabled(issue)) { - if (issue != IssueRegistry.PARSER_ERROR && issue != IssueRegistry.LINT_ERROR) { - mDelegate.log(null, "Incorrect detector reported disabled issue %1$s", - issue.toString()); - } - return; - } - - if (configuration.isIgnored(context, issue, location, message, data)) { - return; - } - - if (severity == Severity.IGNORE) { - return; - } - - mDelegate.report(context, issue, severity, location, message, data); - } - - // Everything else just delegates to the embedding lint client - - @Override - @NonNull - public Configuration getConfiguration(@NonNull Project project) { - return mDelegate.getConfiguration(project); - } - - - @Override - public void log(@NonNull Severity severity, @Nullable Throwable exception, - @Nullable String format, @Nullable Object... args) { - mDelegate.log(exception, format, args); - } - - @Override - @NonNull - public String readFile(@NonNull File file) { - return mDelegate.readFile(file); - } - - @Override - @NonNull - public byte[] readBytes(@NonNull File file) throws IOException { - return mDelegate.readBytes(file); - } - - @Override - @NonNull - public List<File> getJavaSourceFolders(@NonNull Project project) { - return mDelegate.getJavaSourceFolders(project); - } - - @Override - @NonNull - public List<File> getJavaClassFolders(@NonNull Project project) { - return mDelegate.getJavaClassFolders(project); - } - - @NonNull - @Override - public List<File> getJavaLibraries(@NonNull Project project) { - return mDelegate.getJavaLibraries(project); - } - - @Override - @NonNull - public List<File> getResourceFolders(@NonNull Project project) { - return mDelegate.getResourceFolders(project); - } - - @Override - @Nullable - public IDomParser getDomParser() { - return mDelegate.getDomParser(); - } - - @Override - @NonNull - public Class<? extends Detector> replaceDetector( - @NonNull Class<? extends Detector> detectorClass) { - return mDelegate.replaceDetector(detectorClass); - } - - @Override - @NonNull - public SdkInfo getSdkInfo(@NonNull Project project) { - return mDelegate.getSdkInfo(project); - } - - @Override - @NonNull - public Project getProject(@NonNull File dir, @NonNull File referenceDir) { - return mDelegate.getProject(dir, referenceDir); - } - - @Override - @Nullable - public IJavaParser getJavaParser() { - return mDelegate.getJavaParser(); - } - - @Override - public File findResource(@NonNull String relativePath) { - return mDelegate.findResource(relativePath); - } - - @Override - @Nullable - public File getCacheDir(boolean create) { - return mDelegate.getCacheDir(create); - } - - @Override - @NonNull - protected ClassPathInfo getClassPath(@NonNull Project project) { - return mDelegate.getClassPath(project); - } - - @Override - public void log(@Nullable Throwable exception, @Nullable String format, - @Nullable Object... args) { - mDelegate.log(exception, format, args); - } - - @Override - @Nullable - public File getSdkHome() { - return mDelegate.getSdkHome(); - } - - @Override - @NonNull - public IAndroidTarget[] getTargets() { - return mDelegate.getTargets(); - } - - @Override - public int getHighestKnownApiLevel() { - return mDelegate.getHighestKnownApiLevel(); - } - - @Override - @Nullable - public String getSuperClass(@NonNull Project project, @NonNull String name) { - return mDelegate.getSuperClass(project, name); - } - - @Override - @Nullable - public Boolean isSubclassOf(@NonNull Project project, @NonNull String name, - @NonNull String superClassName) { - return mDelegate.isSubclassOf(project, name, superClassName); - } - - @Override - @NonNull - public String getProjectName(@NonNull Project project) { - return mDelegate.getProjectName(project); - } - } - - /** - * Requests another pass through the data for the given detector. This is - * typically done when a detector needs to do more expensive computation, - * but it only wants to do this once it <b>knows</b> that an error is - * present, or once it knows more specifically what to check for. - * - * @param detector the detector that should be included in the next pass. - * Note that the lint runner may refuse to run more than a couple - * of runs. - * @param scope the scope to be revisited. This must be a subset of the - * current scope ({@link #getScope()}, and it is just a performance hint; - * in particular, the detector should be prepared to be called on other - * scopes as well (since they may have been requested by other detectors). - * You can pall null to indicate "all". - */ - public void requestRepeat(@NonNull Detector detector, @Nullable EnumSet<Scope> scope) { - if (mRepeatingDetectors == null) { - mRepeatingDetectors = new ArrayList<Detector>(); - } - mRepeatingDetectors.add(detector); - - if (scope != null) { - if (mRepeatScope == null) { - mRepeatScope = scope; - } else { - mRepeatScope = EnumSet.copyOf(mRepeatScope); - mRepeatScope.addAll(scope); - } - } else { - mRepeatScope = Scope.ALL; - } - } - - // Unfortunately, ASMs nodes do not extend a common DOM node type with parent - // pointers, so we have to have multiple methods which pass in each type - // of node (class, method, field) to be checked. - - /** - * Returns whether the given issue is suppressed in the given method. - * - * @param issue the issue to be checked, or null to just check for "all" - * @param classNode the class containing the issue - * @param method the method containing the issue - * @param instruction the instruction within the method, if any - * @return true if there is a suppress annotation covering the specific - * issue on this method - */ - public boolean isSuppressed( - @Nullable Issue issue, - @NonNull ClassNode classNode, - @NonNull MethodNode method, - @Nullable AbstractInsnNode instruction) { - if (method.invisibleAnnotations != null) { - @SuppressWarnings("unchecked") - List<AnnotationNode> annotations = method.invisibleAnnotations; - return isSuppressed(issue, annotations); - } - - // Initializations of fields end up placed in generated methods (<init> - // for members and <clinit> for static fields). - if (instruction != null && method.name.charAt(0) == '<') { - AbstractInsnNode next = LintUtils.getNextInstruction(instruction); - if (next != null && next.getType() == AbstractInsnNode.FIELD_INSN) { - FieldInsnNode fieldRef = (FieldInsnNode) next; - FieldNode field = findField(classNode, fieldRef.owner, fieldRef.name); - if (field != null && isSuppressed(issue, field)) { - return true; - } - } - } - - return false; - } - - @Nullable - private FieldNode findField(ClassNode classNode, String owner, String name) { - while (classNode != null) { - if (owner.equals(classNode.name)) { - @SuppressWarnings("rawtypes") // ASM API - List fieldList = classNode.fields; - for (Object f : fieldList) { - FieldNode field = (FieldNode) f; - if (field.name.equals(name)) { - return field; - } - } - return null; - } - classNode = getOuterClassNode(classNode); - } - return null; - } - - /** - * Returns whether the given issue is suppressed for the given field. - * - * @param issue the issue to be checked, or null to just check for "all" - * @param field the field potentially annotated with a suppress annotation - * @return true if there is a suppress annotation covering the specific - * issue on this field - */ - public boolean isSuppressed(@Nullable Issue issue, @NonNull FieldNode field) { - if (field.invisibleAnnotations != null) { - @SuppressWarnings("unchecked") - List<AnnotationNode> annotations = field.invisibleAnnotations; - return isSuppressed(issue, annotations); - } - - return false; - } - - /** - * Returns whether the given issue is suppressed in the given class. - * - * @param issue the issue to be checked, or null to just check for "all" - * @param classNode the class containing the issue - * @return true if there is a suppress annotation covering the specific - * issue in this class - */ - public boolean isSuppressed(@Nullable Issue issue, @NonNull ClassNode classNode) { - if (classNode.invisibleAnnotations != null) { - @SuppressWarnings("unchecked") - List<AnnotationNode> annotations = classNode.invisibleAnnotations; - return isSuppressed(issue, annotations); - } - - return false; - } - - private static boolean isSuppressed(@Nullable Issue issue, List<AnnotationNode> annotations) { - for (AnnotationNode annotation : annotations) { - String desc = annotation.desc; - - // We could obey @SuppressWarnings("all") too, but no need to look for it - // because that annotation only has source retention. - - if (desc.endsWith(SUPPRESS_LINT_VMSIG)) { - if (annotation.values != null) { - for (int i = 0, n = annotation.values.size(); i < n; i += 2) { - String key = (String) annotation.values.get(i); - if (key.equals("value")) { //$NON-NLS-1$ - Object value = annotation.values.get(i + 1); - if (value instanceof String) { - String id = (String) value; - if (id.equalsIgnoreCase(SUPPRESS_ALL) || - issue != null && id.equalsIgnoreCase(issue.getId())) { - return true; - } - } else if (value instanceof List) { - @SuppressWarnings("rawtypes") - List list = (List) value; - for (Object v : list) { - if (v instanceof String) { - String id = (String) v; - if (id.equalsIgnoreCase(SUPPRESS_ALL) || (issue != null - && id.equalsIgnoreCase(issue.getId()))) { - return true; - } - } - } - } - } - } - } - } - } - - return false; - } - - /** - * Returns whether the given issue is suppressed in the given parse tree node. - * - * @param issue the issue to be checked, or null to just check for "all" - * @param scope the AST node containing the issue - * @return true if there is a suppress annotation covering the specific - * issue in this class - */ - public boolean isSuppressed(@NonNull Issue issue, @Nullable Node scope) { - while (scope != null) { - Class<? extends Node> type = scope.getClass(); - // The Lombok AST uses a flat hierarchy of node type implementation classes - // so no need to do instanceof stuff here. - if (type == VariableDefinition.class) { - // Variable - VariableDefinition declaration = (VariableDefinition) scope; - if (isSuppressed(issue, declaration.astModifiers())) { - return true; - } - } else if (type == MethodDeclaration.class) { - // Method - // Look for annotations on the method - MethodDeclaration declaration = (MethodDeclaration) scope; - if (isSuppressed(issue, declaration.astModifiers())) { - return true; - } - } else if (type == ConstructorDeclaration.class) { - // Constructor - // Look for annotations on the method - ConstructorDeclaration declaration = (ConstructorDeclaration) scope; - if (isSuppressed(issue, declaration.astModifiers())) { - return true; - } - } else if (type == ClassDeclaration.class) { - // Class - ClassDeclaration declaration = (ClassDeclaration) scope; - if (isSuppressed(issue, declaration.astModifiers())) { - return true; - } - } - - scope = scope.getParent(); - } - - return false; - } - - /** - * Returns true if the given AST modifier has a suppress annotation for the - * given issue (which can be null to check for the "all" annotation) - * - * @param issue the issue to be checked - * @param modifiers the modifier to check - * @return true if the issue or all issues should be suppressed for this - * modifier - */ - private static boolean isSuppressed(@Nullable Issue issue, @Nullable Modifiers modifiers) { - if (modifiers == null) { - return false; - } - StrictListAccessor<Annotation, Modifiers> annotations = modifiers.astAnnotations(); - if (annotations == null) { - return false; - } - - Iterator<Annotation> iterator = annotations.iterator(); - while (iterator.hasNext()) { - Annotation annotation = iterator.next(); - TypeReference t = annotation.astAnnotationTypeReference(); - String typeName = t.getTypeName(); - if (typeName.endsWith(SUPPRESS_LINT) - || typeName.endsWith("SuppressWarnings")) { //$NON-NLS-1$ - StrictListAccessor<AnnotationElement, Annotation> values = - annotation.astElements(); - if (values != null) { - Iterator<AnnotationElement> valueIterator = values.iterator(); - while (valueIterator.hasNext()) { - AnnotationElement element = valueIterator.next(); - AnnotationValue valueNode = element.astValue(); - if (valueNode == null) { - continue; - } - if (valueNode instanceof StringLiteral) { - StringLiteral literal = (StringLiteral) valueNode; - String value = literal.astValue(); - if (value.equalsIgnoreCase(SUPPRESS_ALL) || - issue != null && issue.getId().equalsIgnoreCase(value)) { - return true; - } - } else if (valueNode instanceof ArrayInitializer) { - ArrayInitializer array = (ArrayInitializer) valueNode; - StrictListAccessor<Expression, ArrayInitializer> expressions = - array.astExpressions(); - if (expressions == null) { - continue; - } - Iterator<Expression> arrayIterator = expressions.iterator(); - while (arrayIterator.hasNext()) { - Expression arrayElement = arrayIterator.next(); - if (arrayElement instanceof StringLiteral) { - String value = ((StringLiteral) arrayElement).astValue(); - if (value.equalsIgnoreCase(SUPPRESS_ALL) || (issue != null - && issue.getId().equalsIgnoreCase(value))) { - return true; - } - } - } - } - } - } - } - } - - return false; - } - - /** - * Returns whether the given issue is suppressed in the given XML DOM node. - * - * @param issue the issue to be checked, or null to just check for "all" - * @param node the DOM node containing the issue - * @return true if there is a suppress annotation covering the specific - * issue in this class - */ - public boolean isSuppressed(@NonNull Issue issue, @Nullable org.w3c.dom.Node node) { - if (node instanceof Attr) { - node = ((Attr) node).getOwnerElement(); - } - while (node != null) { - if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) { - Element element = (Element) node; - if (element.hasAttributeNS(TOOLS_URI, ATTR_IGNORE)) { - String ignore = element.getAttributeNS(TOOLS_URI, ATTR_IGNORE); - if (ignore.indexOf(',') == -1) { - if (ignore.equalsIgnoreCase(SUPPRESS_ALL) || (issue != null - && issue.getId().equalsIgnoreCase(ignore))) { - return true; - } - } else { - for (String id : ignore.split(",")) { //$NON-NLS-1$ - if (id.equalsIgnoreCase(SUPPRESS_ALL) || (issue != null - && issue.getId().equalsIgnoreCase(id))) { - return true; - } - } - } - } - } - - node = node.getParentNode(); - } - - return false; - } - - /** A pending class to be analyzed by {@link #checkClasses} */ - @VisibleForTesting - static class ClassEntry implements Comparable<ClassEntry> { - public final File file; - public final File jarFile; - public final File binDir; - public final byte[] bytes; - - public ClassEntry(File file, File jarFile, File binDir, byte[] bytes) { - super(); - this.file = file; - this.jarFile = jarFile; - this.binDir = binDir; - this.bytes = bytes; - } - - public String path() { - if (jarFile != null) { - return jarFile.getPath() + ':' + file.getPath(); - } else { - return file.getPath(); - } - } - - @Override - public int compareTo(ClassEntry other) { - String p1 = file.getPath(); - String p2 = other.file.getPath(); - int m1 = p1.length(); - int m2 = p2.length(); - int m = Math.min(m1, m2); - - for (int i = 0; i < m; i++) { - char c1 = p1.charAt(i); - char c2 = p2.charAt(i); - if (c1 != c2) { - // Sort Foo$Bar.class *after* Foo.class, even though $ < . - if (c1 == '.' && c2 == '$') { - return -1; - } - if (c1 == '$' && c2 == '.') { - return 1; - } - return c1 - c2; - } - } - - return (m == m1) ? -1 : 1; - } - - @Override - public String toString() { - return file.getPath(); - } - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintListener.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintListener.java deleted file mode 100644 index 8195d36..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintListener.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.google.common.annotations.Beta; - -/** - * Interface implemented by listeners to be notified of lint events - * <p> - * <b>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.</b> - */ -@Beta -public interface LintListener { - /** The various types of events provided to lint listeners */ - enum EventType { - /** A lint check is about to begin */ - STARTING, - - /** Lint is about to check the given project, see {@link Context#getProject()} */ - SCANNING_PROJECT, - - /** Lint is about to check the given library project, see {@link Context#getProject()} */ - SCANNING_LIBRARY_PROJECT, - - /** Lint is about to check the given file, see {@link Context#file} */ - SCANNING_FILE, - - /** A new pass was initiated */ - NEW_PHASE, - - /** The lint check was canceled */ - CANCELED, - - /** The lint check is done */ - COMPLETED, - } - - /** - * Notifies listeners that the event of the given type has occurred. - * Additional information, such as the file being scanned, or the project - * being scanned, is available in the {@link Context} object (except for the - * {@link EventType#STARTING}, {@link EventType#CANCELED} or - * {@link EventType#COMPLETED} events which are fired outside of project - * contexts.) - * - * @param driver the driver running through the checks - * @param type the type of event that occurred - * @param context the context providing additional information - */ - void update(@NonNull LintDriver driver, @NonNull EventType type, - @Nullable Context context); -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/OtherFileVisitor.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/OtherFileVisitor.java deleted file mode 100644 index 573d4f0..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/OtherFileVisitor.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2013 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.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.DOT_CLASS; -import static com.android.SdkConstants.DOT_JAVA; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.FD_ASSETS; -import static com.android.tools.lint.detector.api.Detector.OtherFileScanner; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Project; -import com.android.tools.lint.detector.api.Scope; -import com.android.utils.SdkUtils; -import com.google.common.collect.Lists; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; - -/** - * Visitor for "other" files: files that aren't java sources, - * XML sources, etc -- or which should have custom handling in some - * other way. - */ -class OtherFileVisitor { - @NonNull - private final List<Detector> mDetectors; - - @NonNull - private Map<Scope, List<File>> mFiles = new EnumMap<Scope, List<File>>(Scope.class); - - OtherFileVisitor(@NonNull List<Detector> detectors) { - mDetectors = detectors; - } - - /** Analyze other files in the given project */ - void scan( - @NonNull LintDriver driver, - @NonNull Project project, - @Nullable Project main) { - // Collect all project files - File projectFolder = project.getDir(); - - EnumSet<Scope> scopes = EnumSet.noneOf(Scope.class); - for (Detector detector : mDetectors) { - OtherFileScanner fileScanner = (OtherFileScanner) detector; - EnumSet<Scope> applicable = fileScanner.getApplicableFiles(); - if (applicable.contains(Scope.OTHER)) { - scopes = Scope.ALL; - break; - } - scopes.addAll(applicable); - } - - List<File> subset = project.getSubset(); - - if (scopes.contains(Scope.RESOURCE_FILE)) { - if (subset != null && !subset.isEmpty()) { - List<File> files = new ArrayList<File>(subset.size()); - for (File file : subset) { - if (SdkUtils.endsWith(file.getPath(), DOT_XML) && - !file.getName().equals(ANDROID_MANIFEST_XML)) { - files.add(file); - } - } - if (!files.isEmpty()) { - mFiles.put(Scope.RESOURCE_FILE, files); - } - } else { - List<File> files = Lists.newArrayListWithExpectedSize(100); - for (File res : project.getResourceFolders()) { - collectFiles(files, res); - } - File assets = new File(projectFolder, FD_ASSETS); - if (assets.exists()) { - collectFiles(files, assets); - } - if (!files.isEmpty()) { - mFiles.put(Scope.RESOURCE_FILE, files); - } - } - } - - if (scopes.contains(Scope.JAVA_FILE)) { - if (subset != null && !subset.isEmpty()) { - List<File> files = new ArrayList<File>(subset.size()); - for (File file : subset) { - if (file.getPath().endsWith(DOT_JAVA)) { - files.add(file); - } - } - if (!files.isEmpty()) { - mFiles.put(Scope.JAVA_FILE, files); - } - } else { - List<File> files = Lists.newArrayListWithExpectedSize(100); - for (File srcFolder : project.getJavaSourceFolders()) { - collectFiles(files, srcFolder); - } - if (!files.isEmpty()) { - mFiles.put(Scope.JAVA_FILE, files); - } - } - } - - if (scopes.contains(Scope.CLASS_FILE)) { - if (subset != null && !subset.isEmpty()) { - List<File> files = new ArrayList<File>(subset.size()); - for (File file : subset) { - if (file.getPath().endsWith(DOT_CLASS)) { - files.add(file); - } - } - if (!files.isEmpty()) { - mFiles.put(Scope.CLASS_FILE, files); - } - } else { - List<File> files = Lists.newArrayListWithExpectedSize(100); - for (File classFolder : project.getJavaClassFolders()) { - collectFiles(files, classFolder); - } - if (!files.isEmpty()) { - mFiles.put(Scope.CLASS_FILE, files); - } - } - } - - if (scopes.contains(Scope.MANIFEST)) { - if (subset != null && !subset.isEmpty()) { - List<File> files = new ArrayList<File>(subset.size()); - for (File file : subset) { - if (file.getName().equals(ANDROID_MANIFEST_XML)) { - files.add(file); - } - } - if (!files.isEmpty()) { - mFiles.put(Scope.MANIFEST, files); - } - } else { - File manifestFile = project.getManifestFile(); - if (manifestFile != null) { - mFiles.put(Scope.MANIFEST, Collections.<File>singletonList(manifestFile)); - } - } - } - - for (Map.Entry<Scope, List<File>> entry : mFiles.entrySet()) { - Scope scope = entry.getKey(); - List<File> files = entry.getValue(); - List<Detector> applicable = new ArrayList<Detector>(mDetectors.size()); - for (Detector detector : mDetectors) { - OtherFileScanner fileScanner = (OtherFileScanner) detector; - EnumSet<Scope> appliesTo = fileScanner.getApplicableFiles(); - if (appliesTo.contains(Scope.OTHER) || appliesTo.contains(scope)) { - applicable.add(detector); - } - } - if (!applicable.isEmpty()) { - for (File file : files) { - Context context = new Context(driver, project, main, file); - for (Detector detector : applicable) { - detector.beforeCheckFile(context); - detector.run(context); - detector.afterCheckFile(context); - } - if (driver.isCanceled()) { - return; - } - } - } - } - } - - private static void collectFiles(List<File> files, File file) { - if (file.isDirectory()) { - File[] children = file.listFiles(); - if (children != null) { - for (File child : children) { - collectFiles(files, child); - } - } - } else { - files.add(file); - } - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/SdkInfo.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/SdkInfo.java deleted file mode 100644 index 8b3d1e9..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/SdkInfo.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.google.common.annotations.Beta; - -/** - * Information about SDKs - * <p> - * <b>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.</b> - */ -@Beta -public abstract class SdkInfo { - /** - * Returns true if the given child view is the same class or a sub class of - * the given parent view class - * - * @param parentViewFqcn the fully qualified class name of the parent view - * @param childViewFqcn the fully qualified class name of the child view - * @return true if the child view is a sub view of (or the same class as) - * the parent view - */ - public boolean isSubViewOf(@NonNull String parentViewFqcn, @NonNull String childViewFqcn) { - while (!childViewFqcn.equals("android.view.View")) { //$NON-NLS-1$ - if (parentViewFqcn.equals(childViewFqcn)) { - return true; - } - String parent = getParentViewClass(childViewFqcn); - if (parent == null) { - // Unknown view - err on the side of caution - return true; - } - childViewFqcn = parent; - } - - return false; - } - - - /** - * Returns the fully qualified name of the parent view, or null if the view - * is the root android.view.View class. - * - * @param fqcn the fully qualified class name of the view - * @return the fully qualified class name of the parent view, or null - */ - @Nullable - public abstract String getParentViewClass(@NonNull String fqcn); - - /** - * Returns the class name of the parent view, or null if the view is the - * root android.view.View class. This is the same as the - * {@link #getParentViewClass(String)} but without the package. - * - * @param name the view class name to look up the parent for (not including - * package) - * @return the view name of the parent - */ - @Nullable - public abstract String getParentViewName(@NonNull String name); - - /** - * Returns true if the given widget name is a layout - * - * @param tag the XML tag for the view - * @return true if the given tag corresponds to a layout - */ - public boolean isLayout(@NonNull String tag) { - return tag.endsWith("Layout"); //$NON-NLS-1$ - } - - // TODO: Add access to resource resolution here. -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/XmlVisitor.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/XmlVisitor.java deleted file mode 100644 index 2e64118..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/XmlVisitor.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.XmlScanner; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.XmlContext; -import com.google.common.annotations.Beta; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.RandomAccess; - -/** - * Specialized visitor for running detectors on an XML document. - * It operates in two phases: - * <ol> - * <li> First, it computes a set of maps where it generates a map from each - * significant element name, and each significant attribute name, to a list - * of detectors to consult for that element or attribute name. - * The set of element names or attribute names (or both) that a detector - * is interested in is provided by the detectors themselves. - * <li> Second, it iterates over the document a single time. For each element and - * attribute it looks up the list of interested detectors, and runs them. - * </ol> - * It also notifies all the detectors before and after the document is processed - * such that they can do pre- and post-processing. - * <p> - * <b>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.</b> - */ -@Beta -class XmlVisitor { - private final Map<String, List<Detector.XmlScanner>> mElementToCheck = - new HashMap<String, List<Detector.XmlScanner>>(); - private final Map<String, List<Detector.XmlScanner>> mAttributeToCheck = - new HashMap<String, List<Detector.XmlScanner>>(); - private final List<Detector.XmlScanner> mDocumentDetectors = - new ArrayList<Detector.XmlScanner>(); - private final List<Detector.XmlScanner> mAllElementDetectors = - new ArrayList<Detector.XmlScanner>(); - private final List<Detector.XmlScanner> mAllAttributeDetectors = - new ArrayList<Detector.XmlScanner>(); - private final List<? extends Detector> mAllDetectors; - private final IDomParser mParser; - - // Really want this: - //<T extends List<Detector> & Detector.XmlScanner> XmlVisitor(IDomParser parser, - // T xmlDetectors) { - // but it makes client code tricky and ugly. - XmlVisitor(@NonNull IDomParser parser, @NonNull List<? extends Detector> xmlDetectors) { - mParser = parser; - mAllDetectors = xmlDetectors; - - // TODO: Check appliesTo() for files, and find a quick way to enable/disable - // rules when running through a full project! - for (Detector detector : xmlDetectors) { - Detector.XmlScanner xmlDetector = (XmlScanner) detector; - Collection<String> attributes = xmlDetector.getApplicableAttributes(); - if (attributes == XmlScanner.ALL) { - mAllAttributeDetectors.add(xmlDetector); - } else if (attributes != null) { - for (String attribute : attributes) { - List<Detector.XmlScanner> list = mAttributeToCheck.get(attribute); - if (list == null) { - list = new ArrayList<Detector.XmlScanner>(); - mAttributeToCheck.put(attribute, list); - } - list.add(xmlDetector); - } - } - Collection<String> elements = xmlDetector.getApplicableElements(); - if (elements == XmlScanner.ALL) { - mAllElementDetectors.add(xmlDetector); - } else if (elements != null) { - for (String element : elements) { - List<Detector.XmlScanner> list = mElementToCheck.get(element); - if (list == null) { - list = new ArrayList<Detector.XmlScanner>(); - mElementToCheck.put(element, list); - } - list.add(xmlDetector); - } - } - - if ((attributes == null || (attributes.isEmpty() - && attributes != XmlScanner.ALL)) - && (elements == null || (elements.isEmpty() - && elements != XmlScanner.ALL))) { - mDocumentDetectors.add(xmlDetector); - } - } - } - - void visitFile(@NonNull XmlContext context, @NonNull File file) { - assert LintUtils.isXmlFile(file); - context.parser = mParser; - - try { - if (context.document == null) { - context.document = mParser.parseXml(context); - if (context.document == null) { - // No need to log this; the parser should be reporting - // a full warning (such as IssueRegistry#PARSER_ERROR) - // with details, location, etc. - return; - } - if (context.document.getDocumentElement() == null) { - // Ignore empty documents - return; - } - } - - for (Detector check : mAllDetectors) { - check.beforeCheckFile(context); - } - - for (Detector.XmlScanner check : mDocumentDetectors) { - check.visitDocument(context, context.document); - } - - if (!mElementToCheck.isEmpty() || !mAttributeToCheck.isEmpty() - || !mAllAttributeDetectors.isEmpty() || !mAllElementDetectors.isEmpty()) { - visitElement(context, context.document.getDocumentElement()); - } - - for (Detector check : mAllDetectors) { - check.afterCheckFile(context); - } - } finally { - if (context.document != null) { - mParser.dispose(context, context.document); - context.document = null; - } - } - } - - private void visitElement(@NonNull XmlContext context, @NonNull Element element) { - List<Detector.XmlScanner> elementChecks = mElementToCheck.get(element.getTagName()); - if (elementChecks != null) { - assert elementChecks instanceof RandomAccess; - for (XmlScanner check : elementChecks) { - check.visitElement(context, element); - } - } - if (!mAllElementDetectors.isEmpty()) { - for (XmlScanner check : mAllElementDetectors) { - check.visitElement(context, element); - } - } - - if (!mAttributeToCheck.isEmpty() || !mAllAttributeDetectors.isEmpty()) { - NamedNodeMap attributes = element.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Attr attribute = (Attr) attributes.item(i); - String name = attribute.getLocalName(); - if (name == null) { - name = attribute.getName(); - } - List<Detector.XmlScanner> list = mAttributeToCheck.get(name); - if (list != null) { - for (XmlScanner check : list) { - check.visitAttribute(context, attribute); - } - } - if (!mAllAttributeDetectors.isEmpty()) { - for (XmlScanner check : mAllAttributeDetectors) { - check.visitAttribute(context, attribute); - } - } - } - } - - // Visit children - NodeList childNodes = element.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE) { - visitElement(context, (Element) child); - } - } - - // Post hooks - if (elementChecks != null) { - for (XmlScanner check : elementChecks) { - check.visitElementAfter(context, element); - } - } - if (!mAllElementDetectors.isEmpty()) { - for (XmlScanner check : mAllElementDetectors) { - check.visitElementAfter(context, element); - } - } - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Category.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Category.java deleted file mode 100644 index c267420..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Category.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.google.common.annotations.Beta; - -/** - * A category is a container for related issues. - * <p/> - * <b>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.</b> - */ -@Beta -public final class Category implements Comparable<Category> { - private final String mName; - private final int mPriority; - private final Category mParent; - - /** - * Creates a new {@link Category}. - * - * @param parent the name of a parent category, or null - * @param name the name of the category - * @param priority a sorting priority, with higher being more important - */ - private Category( - @Nullable Category parent, - @NonNull String name, - int priority) { - mParent = parent; - mName = name; - mPriority = priority; - } - - /** - * Creates a new top level {@link Category} with the given sorting priority. - * - * @param name the name of the category - * @param priority a sorting priority, with higher being more important - * @return a new category - */ - @NonNull - public static Category create(@NonNull String name, int priority) { - return new Category(null, name, priority); - } - - /** - * Creates a new top level {@link Category} with the given sorting priority. - * - * @param parent the name of a parent category, or null - * @param name the name of the category - * @param priority a sorting priority, with higher being more important - * @return a new category - */ - @NonNull - public static Category create(@Nullable Category parent, @NonNull String name, int priority) { - return new Category(parent, name, priority); - } - - /** - * Returns the parent category, or null if this is a top level category - * - * @return the parent category, or null if this is a top level category - */ - public Category getParent() { - return mParent; - } - - /** - * Returns the name of this category - * - * @return the name of this category - */ - public String getName() { - return mName; - } - - /** - * Returns a full name for this category. For a top level category, this is just - * the {@link #getName()} value, but for nested categories it will include the parent - * names as well. - * - * @return a full name for this category - */ - public String getFullName() { - if (mParent != null) { - return mParent.getFullName() + ':' + mName; - } else { - return mName; - } - } - - @Override - public int compareTo(Category other) { - if (other.mPriority == mPriority) { - if (mParent == other) { - return 1; - } else if (other.mParent == this) { - return -1; - } - } - return other.mPriority - mPriority; - } - - /** Issues related to running lint itself */ - public static final Category LINT = create("Lint", 110); - - /** Issues related to correctness */ - public static final Category CORRECTNESS = create("Correctness", 100); - - /** Issues related to security */ - public static final Category SECURITY = create("Security", 90); - - /** Issues related to performance */ - public static final Category PERFORMANCE = create("Performance", 80); - - /** Issues related to usability */ - public static final Category USABILITY = create("Usability", 70); - - /** Issues related to accessibility */ - public static final Category A11Y = create("Accessibility", 60); - - /** Issues related to internationalization */ - public static final Category I18N = create("Internationalization", 50); - - // Sub categories - - /** Issues related to icons */ - public static final Category ICONS = create(USABILITY, "Icons", 73); - - /** Issues related to typography */ - public static final Category TYPOGRAPHY = create(USABILITY, "Typography", 76); - - /** Issues related to messages/strings */ - public static final Category MESSAGES = create(CORRECTNESS, "Messages", 95); -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/ClassContext.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/ClassContext.java deleted file mode 100644 index 5150eff..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/ClassContext.java +++ /dev/null @@ -1,682 +0,0 @@ -/* - * 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.detector.api; - -import static com.android.SdkConstants.CONSTRUCTOR_NAME; -import static com.android.SdkConstants.DOT_CLASS; -import static com.android.SdkConstants.DOT_JAVA; -import static com.android.tools.lint.detector.api.Location.SearchDirection.BACKWARD; -import static com.android.tools.lint.detector.api.Location.SearchDirection.EOL_BACKWARD; -import static com.android.tools.lint.detector.api.Location.SearchDirection.FORWARD; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.LintDriver; -import com.android.tools.lint.detector.api.Location.SearchDirection; -import com.android.tools.lint.detector.api.Location.SearchHints; -import com.google.common.annotations.Beta; -import com.google.common.base.Splitter; - -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.LineNumberNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; - -import java.io.File; -import java.util.List; - -/** - * A {@link Context} used when checking .class files. - * <p/> - * <b>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.</b> - */ -@Beta -public class ClassContext extends Context { - private final File mBinDir; - /** The class file DOM root node */ - private final ClassNode mClassNode; - /** The class file byte data */ - private final byte[] mBytes; - /** The source file, if known/found */ - private File mSourceFile; - /** The contents of the source file, if source file is known/found */ - private String mSourceContents; - /** Whether we've searched for the source file (used to avoid repeated failed searches) */ - private boolean mSearchedForSource; - /** If the file is a relative path within a jar file, this is the jar file, otherwise null */ - private final File mJarFile; - /** Whether this class is part of a library (rather than corresponding to one of the - * source files in this project */ - private final boolean mFromLibrary; - - /** - * Construct a new {@link ClassContext} - * - * @param driver the driver running through the checks - * @param project the project containing the file being checked - * @param main the main project if this project is a library project, or - * null if this is not a library project. The main project is the - * root project of all library projects, not necessarily the - * directly including project. - * @param file the file being checked - * @param jarFile If the file is a relative path within a jar file, this is - * the jar file, otherwise null - * @param binDir the root binary directory containing this .class file. - * @param bytes the bytecode raw data - * @param classNode the bytecode object model - * @param fromLibrary whether this class is from a library rather than part - * of this project - * @param sourceContents initial contents of the Java source, if known, or - * null - */ - public ClassContext( - @NonNull LintDriver driver, - @NonNull Project project, - @Nullable Project main, - @NonNull File file, - @Nullable File jarFile, - @NonNull File binDir, - @NonNull byte[] bytes, - @NonNull ClassNode classNode, - boolean fromLibrary, - @Nullable String sourceContents) { - super(driver, project, main, file); - mJarFile = jarFile; - mBinDir = binDir; - mBytes = bytes; - mClassNode = classNode; - mFromLibrary = fromLibrary; - mSourceContents = sourceContents; - } - - /** - * Returns the raw bytecode data for this class file - * - * @return the byte array containing the bytecode data - */ - @NonNull - public byte[] getBytecode() { - return mBytes; - } - - /** - * Returns the bytecode object model - * - * @return the bytecode object model, never null - */ - @NonNull - public ClassNode getClassNode() { - return mClassNode; - } - - /** - * Returns the jar file, if any. If this is null, the .class file is a real file - * on disk, otherwise it represents a relative path within the jar file. - * - * @return the jar file, or null - */ - @Nullable - public File getJarFile() { - return mJarFile; - } - - /** - * Returns whether this class is part of a library (not this project). - * - * @return true if this class is part of a library - */ - public boolean isFromClassLibrary() { - return mFromLibrary; - } - - /** - * Returns the source file for this class file, if possible. - * - * @return the source file, or null - */ - @Nullable - public File getSourceFile() { - if (mSourceFile == null && !mSearchedForSource) { - mSearchedForSource = true; - - String source = mClassNode.sourceFile; - if (source == null) { - source = file.getName(); - if (source.endsWith(DOT_CLASS)) { - source = source.substring(0, source.length() - DOT_CLASS.length()) + DOT_JAVA; - } - int index = source.indexOf('$'); - if (index != -1) { - source = source.substring(0, index) + DOT_JAVA; - } - } - if (source != null) { - if (mJarFile != null) { - String relative = file.getParent() + File.separator + source; - List<File> sources = getProject().getJavaSourceFolders(); - for (File dir : sources) { - File sourceFile = new File(dir, relative); - if (sourceFile.exists()) { - mSourceFile = sourceFile; - break; - } - } - } else { - // Determine package - String topPath = mBinDir.getPath(); - String parentPath = file.getParentFile().getPath(); - if (parentPath.startsWith(topPath)) { - int start = topPath.length() + 1; - String relative = start > parentPath.length() ? // default package? - "" : parentPath.substring(start); - List<File> sources = getProject().getJavaSourceFolders(); - for (File dir : sources) { - File sourceFile = new File(dir, relative + File.separator + source); - if (sourceFile.exists()) { - mSourceFile = sourceFile; - break; - } - } - } - } - } - } - - return mSourceFile; - } - - /** - * Returns the contents of the source file for this class file, if found. - * - * @return the source contents, or "" - */ - @NonNull - public String getSourceContents() { - if (mSourceContents == null) { - File sourceFile = getSourceFile(); - if (sourceFile != null) { - mSourceContents = getClient().readFile(mSourceFile); - } - - if (mSourceContents == null) { - mSourceContents = ""; - } - } - - return mSourceContents; - } - - /** - * Returns the contents of the source file for this class file, if found. If - * {@code read} is false, do not read the source contents if it has not - * already been read. (This is primarily intended for the lint - * infrastructure; most client code would call {@link #getSourceContents()} - * .) - * - * @param read whether to read the source contents if it has not already - * been initialized - * @return the source contents, which will never be null if {@code read} is - * true, or null if {@code read} is false and the source contents - * hasn't already been read. - */ - @Nullable - public String getSourceContents(boolean read) { - if (read) { - return getSourceContents(); - } else { - return mSourceContents; - } - } - - /** - * Returns a location for the given source line number in this class file's - * source file, if available. - * - * @param line the line number (1-based, which is what ASM uses) - * @param patternStart optional pattern to search for in the source for - * range start - * @param patternEnd optional pattern to search for in the source for range - * end - * @param hints additional hints about the pattern search (provided - * {@code patternStart} is non null) - * @return a location, never null - */ - @NonNull - public Location getLocationForLine(int line, @Nullable String patternStart, - @Nullable String patternEnd, @Nullable SearchHints hints) { - File sourceFile = getSourceFile(); - if (sourceFile != null) { - // ASM line numbers are 1-based, and lint line numbers are 0-based - if (line != -1) { - return Location.create(sourceFile, getSourceContents(), line - 1, - patternStart, patternEnd, hints); - } else { - return Location.create(sourceFile); - } - } - - return Location.create(file); - } - - /** - * Reports an issue. - * <p> - * Detectors should only call this method if an error applies to the whole class - * scope and there is no specific method or field that applies to the error. - * If so, use - * {@link #report(Issue, MethodNode, AbstractInsnNode, Location, String, Object)} or - * {@link #report(Issue, FieldNode, Location, String, Object)}, such that - * suppress annotations are checked. - * - * @param issue the issue to report - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - @Override - public void report( - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - if (mDriver.isSuppressed(issue, mClassNode)) { - return; - } - ClassNode curr = mClassNode; - while (curr != null) { - ClassNode prev = curr; - curr = mDriver.getOuterClassNode(curr); - if (curr != null) { - if (prev.outerMethod != null) { - @SuppressWarnings("rawtypes") // ASM API - List methods = curr.methods; - for (Object m : methods) { - MethodNode method = (MethodNode) m; - if (method.name.equals(prev.outerMethod) - && method.desc.equals(prev.outerMethodDesc)) { - // Found the outer method for this anonymous class; continue - // reporting on it (which will also work its way up the parent - // class hierarchy) - if (method != null && mDriver.isSuppressed(issue, mClassNode, method, - null)) { - return; - } - break; - } - } - } - if (mDriver.isSuppressed(issue, curr)) { - return; - } - } - } - - super.report(issue, location, message, data); - } - - // Unfortunately, ASMs nodes do not extend a common DOM node type with parent - // pointers, so we have to have multiple methods which pass in each type - // of node (class, method, field) to be checked. - - /** - * Reports an issue applicable to a given method node. - * - * @param issue the issue to report - * @param method the method scope the error applies to. The lint - * infrastructure will check whether there are suppress - * annotations on this method (or its enclosing class) and if so - * suppress the warning without involving the client. - * @param instruction the instruction within the method the error applies - * to. You cannot place annotations on individual method - * instructions (for example, annotations on local variables are - * allowed, but are not kept in the .class file). However, this - * instruction is needed to handle suppressing errors on field - * initializations; in that case, the errors may be reported in - * the {@code <clinit>} method, but the annotation is found not - * on that method but for the {@link FieldNode}'s. - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - public void report( - @NonNull Issue issue, - @Nullable MethodNode method, - @Nullable AbstractInsnNode instruction, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - if (method != null && mDriver.isSuppressed(issue, mClassNode, method, instruction)) { - return; - } - report(issue, location, message, data); // also checks the class node - } - - /** - * Reports an issue applicable to a given method node. - * - * @param issue the issue to report - * @param field the scope the error applies to. The lint infrastructure - * will check whether there are suppress annotations on this field (or its enclosing - * class) and if so suppress the warning without involving the client. - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - public void report( - @NonNull Issue issue, - @Nullable FieldNode field, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - if (field != null && mDriver.isSuppressed(issue, field)) { - return; - } - report(issue, location, message, data); // also checks the class node - } - - /** - * Finds the line number closest to the given node - * - * @param node the instruction node to get a line number for - * @return the closest line number, or -1 if not known - */ - public static int findLineNumber(@NonNull AbstractInsnNode node) { - AbstractInsnNode curr = node; - - // First search backwards - while (curr != null) { - if (curr.getType() == AbstractInsnNode.LINE) { - return ((LineNumberNode) curr).line; - } - curr = curr.getPrevious(); - } - - // Then search forwards - curr = node; - while (curr != null) { - if (curr.getType() == AbstractInsnNode.LINE) { - return ((LineNumberNode) curr).line; - } - curr = curr.getNext(); - } - - return -1; - } - - /** - * Finds the line number closest to the given method declaration - * - * @param node the method node to get a line number for - * @return the closest line number, or -1 if not known - */ - public static int findLineNumber(@NonNull MethodNode node) { - if (node.instructions != null && node.instructions.size() > 0) { - return findLineNumber(node.instructions.get(0)); - } - - return -1; - } - - /** - * Finds the line number closest to the given class declaration - * - * @param node the method node to get a line number for - * @return the closest line number, or -1 if not known - */ - public static int findLineNumber(@NonNull ClassNode node) { - if (node.methods != null && !node.methods.isEmpty()) { - MethodNode firstMethod = getFirstRealMethod(node); - if (firstMethod != null) { - return findLineNumber(firstMethod); - } - } - - return -1; - } - - /** - * Returns a location for the given {@link ClassNode}, where class node is - * either the top level class, or an inner class, in the current context. - * - * @param classNode the class in the current context - * @return a location pointing to the class declaration, or as close to it - * as possible - */ - @NonNull - public Location getLocation(@NonNull ClassNode classNode) { - // Attempt to find a proper location for this class. This is tricky - // since classes do not have line number entries in the class file; we need - // to find a method, look up the corresponding line number then search - // around it for a suitable tag, such as the class name. - String pattern; - if (isAnonymousClass(classNode.name)) { - pattern = classNode.superName; - } else { - pattern = classNode.name; - } - int index = pattern.lastIndexOf('$'); - if (index != -1) { - pattern = pattern.substring(index + 1); - } - index = pattern.lastIndexOf('/'); - if (index != -1) { - pattern = pattern.substring(index + 1); - } - - return getLocationForLine(findLineNumber(classNode), pattern, null, - SearchHints.create(BACKWARD).matchJavaSymbol()); - } - - @Nullable - private static MethodNode getFirstRealMethod(@NonNull ClassNode classNode) { - // Return the first method in the class for line number purposes. Skip <init>, - // since it's typically not located near the real source of the method. - if (classNode.methods != null) { - @SuppressWarnings("rawtypes") // ASM API - List methods = classNode.methods; - for (Object m : methods) { - MethodNode method = (MethodNode) m; - if (method.name.charAt(0) != '<') { - return method; - } - } - - if (!classNode.methods.isEmpty()) { - return (MethodNode) classNode.methods.get(0); - } - } - - return null; - } - - /** - * Returns a location for the given {@link MethodNode}. - * - * @param methodNode the class in the current context - * @param classNode the class containing the method - * @return a location pointing to the class declaration, or as close to it - * as possible - */ - @NonNull - public Location getLocation(@NonNull MethodNode methodNode, - @NonNull ClassNode classNode) { - // Attempt to find a proper location for this class. This is tricky - // since classes do not have line number entries in the class file; we need - // to find a method, look up the corresponding line number then search - // around it for a suitable tag, such as the class name. - String pattern; - SearchDirection searchMode; - if (methodNode.name.equals(CONSTRUCTOR_NAME)) { - searchMode = EOL_BACKWARD; - if (isAnonymousClass(classNode.name)) { - pattern = classNode.superName.substring(classNode.superName.lastIndexOf('/') + 1); - } else { - pattern = classNode.name.substring(classNode.name.lastIndexOf('$') + 1); - } - } else { - searchMode = BACKWARD; - pattern = methodNode.name; - } - - return getLocationForLine(findLineNumber(methodNode), pattern, null, - SearchHints.create(searchMode).matchJavaSymbol()); - } - - /** - * Returns a location for the given {@link AbstractInsnNode}. - * - * @param instruction the instruction to look up the location for - * @return a location pointing to the instruction, or as close to it - * as possible - */ - @NonNull - public Location getLocation(@NonNull AbstractInsnNode instruction) { - SearchHints hints = SearchHints.create(FORWARD).matchJavaSymbol(); - String pattern = null; - if (instruction instanceof MethodInsnNode) { - MethodInsnNode call = (MethodInsnNode) instruction; - if (call.name.equals(CONSTRUCTOR_NAME)) { - pattern = call.owner; - hints = hints.matchConstructor(); - } else { - pattern = call.name; - } - int index = pattern.lastIndexOf('$'); - if (index != -1) { - pattern = pattern.substring(index + 1); - } - index = pattern.lastIndexOf('/'); - if (index != -1) { - pattern = pattern.substring(index + 1); - } - } - - int line = findLineNumber(instruction); - return getLocationForLine(line, pattern, null, hints); - } - - private static boolean isAnonymousClass(@NonNull String fqcn) { - int lastIndex = fqcn.lastIndexOf('$'); - if (lastIndex != -1 && lastIndex < fqcn.length() - 1) { - if (Character.isDigit(fqcn.charAt(lastIndex + 1))) { - return true; - } - } - return false; - } - - /** - * Converts from a VM owner name (such as foo/bar/Foo$Baz) to a - * fully qualified class name (such as foo.bar.Foo.Baz). - * - * @param owner the owner name to convert - * @return the corresponding fully qualified class name - */ - @NonNull - public static String getFqcn(@NonNull String owner) { - return owner.replace('/', '.').replace('$','.'); - } - - /** - * Computes a user-readable type signature from the given class owner, name - * and description. For example, for owner="foo/bar/Foo$Baz", name="foo", - * description="(I)V", it returns "void foo.bar.Foo.Bar#foo(int)". - * - * @param owner the class name - * @param name the method name - * @param desc the method description - * @return a user-readable string - */ - public static String createSignature(String owner, String name, String desc) { - StringBuilder sb = new StringBuilder(100); - - if (desc != null) { - Type returnType = Type.getReturnType(desc); - sb.append(getTypeString(returnType)); - sb.append(' '); - } - - if (owner != null) { - sb.append(getFqcn(owner)); - } - if (name != null) { - sb.append('#'); - sb.append(name); - if (desc != null) { - Type[] argumentTypes = Type.getArgumentTypes(desc); - if (argumentTypes != null && argumentTypes.length > 0) { - sb.append('('); - boolean first = true; - for (Type type : argumentTypes) { - if (first) { - first = false; - } else { - sb.append(", "); - } - sb.append(getTypeString(type)); - } - sb.append(')'); - } - } - } - - return sb.toString(); - } - - private static String getTypeString(Type type) { - String s = type.getClassName(); - if (s.startsWith("java.lang.")) { //$NON-NLS-1$ - s = s.substring("java.lang.".length()); //$NON-NLS-1$ - } - - return s; - } - - /** - * Computes the internal class name of the given fully qualified class name. - * For example, it converts foo.bar.Foo.Bar into foo/bar/Foo$Bar - * - * @param fqcn the fully qualified class name - * @return the internal class name - */ - @NonNull - public static String getInternalName(@NonNull String fqcn) { - if (fqcn.indexOf('.') == -1) { - return fqcn; - } - - StringBuilder sb = new StringBuilder(fqcn.length()); - String prev = null; - for (String part : Splitter.on('.').split(fqcn)) { - if (prev != null && prev.length() > 0) { - if (Character.isUpperCase(prev.charAt(0))) { - sb.append('$'); - } else { - sb.append('/'); - } - } - sb.append(part); - prev = part; - } - - return sb.toString(); - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Context.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Context.java deleted file mode 100644 index a379dd0..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Context.java +++ /dev/null @@ -1,377 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.Configuration; -import com.android.tools.lint.client.api.LintClient; -import com.android.tools.lint.client.api.LintDriver; -import com.android.tools.lint.client.api.SdkInfo; -import com.google.common.annotations.Beta; -import com.google.common.base.Splitter; - -import java.io.File; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Context passed to the detectors during an analysis run. It provides - * information about the file being analyzed, it allows shared properties (so - * the detectors can share results), etc. - * <p> - * <b>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.</b> - */ -@Beta -public class Context { - /** - * The file being checked. Note that this may not always be to a concrete - * file. For example, in the {@link Detector#beforeCheckProject(Context)} - * method, the context file is the directory of the project. - */ - public final File file; - - /** The driver running through the checks */ - protected final LintDriver mDriver; - - /** The project containing the file being checked */ - @NonNull - private final Project mProject; - - /** - * The "main" project. For normal projects, this is the same as {@link #mProject}, - * but for library projects, it's the root project that includes (possibly indirectly) - * the various library projects and their library projects. - * <p> - * Note that this is a property on the {@link Context}, not the - * {@link Project}, since a library project can be included from multiple - * different top level projects, so there isn't <b>one</b> main project, - * just one per main project being analyzed with its library projects. - */ - private final Project mMainProject; - - /** The current configuration controlling which checks are enabled etc */ - private final Configuration mConfiguration; - - /** The contents of the file */ - private String mContents; - - /** - * Whether the lint job has been canceled. - * <p> - * Slow-running detectors should check this flag via - * {@link AtomicBoolean#get()} and abort if canceled - */ - @NonNull - public final AtomicBoolean canceled = new AtomicBoolean(); - - /** Map of properties to share results between detectors */ - private Map<String, Object> mProperties; - - /** - * Construct a new {@link Context} - * - * @param driver the driver running through the checks - * @param project the project containing the file being checked - * @param main the main project if this project is a library project, or - * null if this is not a library project. The main project is - * the root project of all library projects, not necessarily the - * directly including project. - * @param file the file being checked - */ - public Context( - @NonNull LintDriver driver, - @NonNull Project project, - @Nullable Project main, - @NonNull File file) { - this.file = file; - - mDriver = driver; - mProject = project; - mMainProject = main; - mConfiguration = project.getConfiguration(); - } - - /** - * Returns the scope for the lint job - * - * @return the scope, never null - */ - @NonNull - public EnumSet<Scope> getScope() { - return mDriver.getScope(); - } - - /** - * Returns the configuration for this project. - * - * @return the configuration, never null - */ - @NonNull - public Configuration getConfiguration() { - return mConfiguration; - } - - /** - * Returns the project containing the file being checked - * - * @return the project, never null - */ - @NonNull - public Project getProject() { - return mProject; - } - - /** - * Returns the main project if this project is a library project, or self - * if this is not a library project. The main project is the root project - * of all library projects, not necessarily the directly including project. - * - * @return the main project, never null - */ - @NonNull - public Project getMainProject() { - return mMainProject != null ? mMainProject : mProject; - } - - /** - * Returns the lint client requesting the lint check - * - * @return the client, never null - */ - @NonNull - public LintClient getClient() { - return mDriver.getClient(); - } - - /** - * Returns the driver running through the lint checks - * - * @return the driver - */ - @NonNull - public LintDriver getDriver() { - return mDriver; - } - - /** - * Returns the contents of the file. This may not be the contents of the - * file on disk, since it delegates to the {@link LintClient}, which in turn - * may decide to return the current edited contents of the file open in an - * editor. - * - * @return the contents of the given file, or null if an error occurs. - */ - @Nullable - public String getContents() { - if (mContents == null) { - mContents = mDriver.getClient().readFile(file); - } - - return mContents; - } - - /** - * Returns the value of the given named property, or null. - * - * @param name the name of the property - * @return the corresponding value, or null - */ - @Nullable - public Object getProperty(String name) { - if (mProperties == null) { - return null; - } - - return mProperties.get(name); - } - - /** - * Sets the value of the given named property. - * - * @param name the name of the property - * @param value the corresponding value - */ - public void setProperty(@NonNull String name, @Nullable Object value) { - if (value == null) { - if (mProperties != null) { - mProperties.remove(name); - } - } else { - if (mProperties == null) { - mProperties = new HashMap<String, Object>(); - } - mProperties.put(name, value); - } - } - - /** - * Gets the SDK info for the current project. - * - * @return the SDK info for the current project, never null - */ - @NonNull - public SdkInfo getSdkInfo() { - return mProject.getSdkInfo(); - } - - // ---- Convenience wrappers ---- (makes the detector code a bit leaner) - - /** - * Returns false if the given issue has been disabled. Convenience wrapper - * around {@link Configuration#getSeverity(Issue)}. - * - * @param issue the issue to check - * @return false if the issue has been disabled - */ - public boolean isEnabled(@NonNull Issue issue) { - return mConfiguration.isEnabled(issue); - } - - /** - * Reports an issue. Convenience wrapper around {@link LintClient#report} - * - * @param issue the issue to report - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - public void report( - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - Configuration configuration = mConfiguration; - - // If this error was computed for a context where the context corresponds to - // a project instead of a file, the actual error may be in a different project (e.g. - // a library project), so adjust the configuration as necessary. - if (location != null && location.getFile() != null) { - Project project = mDriver.findProjectFor(location.getFile()); - if (project != null) { - configuration = project.getConfiguration(); - } - } - - // If an error occurs in a library project, but you've disabled that check in the - // main project, disable it in the library project too. (In some cases you don't - // control the lint.xml of a library project, and besides, if you're not interested in - // a check for your main project you probably don't care about it in the library either.) - if (configuration != mConfiguration - && mConfiguration.getSeverity(issue) == Severity.IGNORE) { - return; - } - - Severity severity = configuration.getSeverity(issue); - if (severity == Severity.IGNORE) { - return; - } - - mDriver.getClient().report(this, issue, severity, location, message, data); - } - - /** - * Send an exception to the log. Convenience wrapper around {@link LintClient#log}. - * - * @param exception the exception, possibly null - * @param format the error message using {@link String#format} syntax, possibly null - * @param args any arguments for the format string - */ - public void log( - @Nullable Throwable exception, - @Nullable String format, - @Nullable Object... args) { - mDriver.getClient().log(exception, format, args); - } - - /** - * Returns the current phase number. The first pass is numbered 1. Only one pass - * will be performed, unless a {@link Detector} calls {@link #requestRepeat}. - * - * @return the current phase, usually 1 - */ - public int getPhase() { - return mDriver.getPhase(); - } - - /** - * Requests another pass through the data for the given detector. This is - * typically done when a detector needs to do more expensive computation, - * but it only wants to do this once it <b>knows</b> that an error is - * present, or once it knows more specifically what to check for. - * - * @param detector the detector that should be included in the next pass. - * Note that the lint runner may refuse to run more than a couple - * of runs. - * @param scope the scope to be revisited. This must be a subset of the - * current scope ({@link #getScope()}, and it is just a performance hint; - * in particular, the detector should be prepared to be called on other - * scopes as well (since they may have been requested by other detectors). - * You can pall null to indicate "all". - */ - public void requestRepeat(@NonNull Detector detector, @Nullable EnumSet<Scope> scope) { - mDriver.requestRepeat(detector, scope); - } - - /** Pattern for version qualifiers */ - private static final Pattern VERSION_PATTERN = Pattern.compile("^v(\\d+)$"); //$NON-NLS-1$ - - private static File sCachedFolder = null; - private static int sCachedFolderVersion = -1; - - /** - * Returns the folder version. For example, for the file values-v14/foo.xml, - * it returns 14. - * - * @return the folder version, or -1 if no specific version was specified - */ - public int getFolderVersion() { - return getFolderVersion(file); - } - - /** - * Returns the folder version of the given file. For example, for the file values-v14/foo.xml, - * it returns 14. - * - * @param file the file to be checked - * @return the folder version, or -1 if no specific version was specified - */ - public static int getFolderVersion(File file) { - File parent = file.getParentFile(); - if (parent.equals(sCachedFolder)) { - return sCachedFolderVersion; - } - - sCachedFolder = parent; - sCachedFolderVersion = -1; - - for (String qualifier : Splitter.on('-').split(parent.getName())) { - Matcher matcher = VERSION_PATTERN.matcher(qualifier); - if (matcher.matches()) { - sCachedFolderVersion = Integer.parseInt(matcher.group(1)); - break; - } - } - - return sCachedFolderVersion; - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/DefaultPosition.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/DefaultPosition.java deleted file mode 100644 index 72c8ee7..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/DefaultPosition.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.detector.api; - -import com.google.common.annotations.Beta; - -/** - * A simple offset-based position * - * <p> - * <b>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.</b> - */ -@Beta -public class DefaultPosition extends Position { - /** The line number (0-based where the first line is line 0) */ - private final int mLine; - - /** - * The column number (where the first character on the line is 0), or -1 if - * unknown - */ - private final int mColumn; - - /** The character offset */ - private final int mOffset; - - /** - * Creates a new {@link DefaultPosition} - * - * @param line the 0-based line number, or -1 if unknown - * @param column the 0-based column number, or -1 if unknown - * @param offset the offset, or -1 if unknown - */ - public DefaultPosition(int line, int column, int offset) { - mLine = line; - mColumn = column; - mOffset = offset; - } - - @Override - public int getLine() { - return mLine; - } - - @Override - public int getOffset() { - return mOffset; - } - - @Override - public int getColumn() { - return mColumn; - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Detector.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Detector.java deleted file mode 100644 index a3412cf..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Detector.java +++ /dev/null @@ -1,611 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.client.api.LintDriver; -import com.google.common.annotations.Beta; -import lombok.ast.AstVisitor; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.List; - -/** - * A detector is able to find a particular problem. It might also be thought of as enforcing - * a rule, but "rule" is a bit overloaded in ADT terminology since ViewRules are used in - * the Rules API to allow views to specify designtime behavior in the graphical layout editor. - * <p> - * Each detector provides information about the issues it can find, such as an explanation - * of how to fix the issue, the priority, the category, etc. It also has an id which is - * used to persistently identify a particular type of error. - * <p> - * Detectors will be called in a predefined order: - * <ol> - * <li> Manifest file - * <li> Resource files, in alphabetical order by resource type - * (therefore, "layout" is checked before "values", "values-de" is checked before - * "values-en" but after "values", and so on. - * <li> Java sources - * <li> Java classes - * <li> Proguard files - * </ol> - * If a detector needs information when processing a file type that comes from a type of - * file later in the order above, they can request a second phase; see - * {@link LintDriver#requestRepeat}. - * <p> - * NOTE: Detectors might be constructed just once and shared between lint runs, so - * any per-detector state should be initialized and reset via the before/after - * methods. - * <p/> - * <b>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.</b> - */ -@Beta -public abstract class Detector { - /** Specialized interface for detectors that scan Java source file parse trees */ - public interface JavaScanner { - /** - * Create a parse tree visitor to process the parse tree. All - * {@link JavaScanner} detectors must provide a visitor, unless they - * either return true from {@link #appliesToResourceRefs()} or return - * non null from {@link #getApplicableMethodNames()}. - * <p> - * If you return specific AST node types from - * {@link #getApplicableNodeTypes()}, then the visitor will <b>only</b> - * be called for the specific requested node types. This is more - * efficient, since it allows many detectors that apply to only a small - * part of the AST (such as method call nodes) to share iteration of the - * majority of the parse tree. - * <p> - * If you return null from {@link #getApplicableNodeTypes()}, then your - * visitor will be called from the top and all node types visited. - * <p> - * Note that a new visitor is created for each separate compilation - * unit, so you can store per file state in the visitor. - * - * @param context the {@link Context} for the file being analyzed - * @return a visitor, or null. - */ - @Nullable - AstVisitor createJavaVisitor(@NonNull JavaContext context); - - /** - * Return the types of AST nodes that the visitor returned from - * {@link #createJavaVisitor(JavaContext)} should visit. See the - * documentation for {@link #createJavaVisitor(JavaContext)} for details - * on how the shared visitor is used. - * <p> - * If you return null from this method, then the visitor will process - * the full tree instead. - * <p> - * Note that for the shared visitor, the return codes from the visit - * methods are ignored: returning true will <b>not</b> prune iteration - * of the subtree, since there may be other node types interested in the - * children. If you need to ensure that your visitor only processes a - * part of the tree, use a full visitor instead. See the - * OverdrawDetector implementation for an example of this. - * - * @return the list of applicable node types (AST node classes), or null - */ - @Nullable - List<Class<? extends Node>> getApplicableNodeTypes(); - - /** - * Return the list of method names this detector is interested in, or - * null. If this method returns non-null, then any AST nodes that match - * a method call in the list will be passed to the - * {@link #visitMethod(JavaContext, AstVisitor, MethodInvocation)} - * method for processing. The visitor created by - * {@link #createJavaVisitor(JavaContext)} is also passed to that - * method, although it can be null. - * <p> - * This makes it easy to write detectors that focus on some fixed calls. - * For example, the StringFormatDetector uses this mechanism to look for - * "format" calls, and when found it looks around (using the AST's - * {@link Node#getParent()} method) to see if it's called on - * a String class instance, and if so do its normal processing. Note - * that since it doesn't need to do any other AST processing, that - * detector does not actually supply a visitor. - * - * @return a set of applicable method names, or null. - */ - @Nullable - List<String> getApplicableMethodNames(); - - /** - * Method invoked for any method calls found that matches any names - * returned by {@link #getApplicableMethodNames()}. This also passes - * back the visitor that was created by - * {@link #createJavaVisitor(JavaContext)}, but a visitor is not - * required. It is intended for detectors that need to do additional AST - * processing, but also want the convenience of not having to look for - * method names on their own. - * - * @param context the context of the lint request - * @param visitor the visitor created from - * {@link #createJavaVisitor(JavaContext)}, or null - * @param node the {@link MethodInvocation} node for the invoked method - */ - void visitMethod( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull MethodInvocation node); - - /** - * Returns whether this detector cares about Android resource references - * (such as {@code R.layout.main} or {@code R.string.app_name}). If it - * does, then the visitor will look for these patterns, and if found, it - * will invoke {@link #visitResourceReference} passing the resource type - * and resource name. It also passes the visitor, if any, that was - * created by {@link #createJavaVisitor(JavaContext)}, such that a - * detector can do more than just look for resources. - * - * @return true if this detector wants to be notified of R resource - * identifiers found in the code. - */ - boolean appliesToResourceRefs(); - - /** - * Called for any resource references (such as {@code R.layout.main} - * found in Java code, provided this detector returned {@code true} from - * {@link #appliesToResourceRefs()}. - * - * @param context the lint scanning context - * @param visitor the visitor created from - * {@link #createJavaVisitor(JavaContext)}, or null - * @param node the variable reference for the resource - * @param type the resource type, such as "layout" or "string" - * @param name the resource name, such as "main" from - * {@code R.layout.main} - * @param isFramework whether the resource is a framework resource - * (android.R) or a local project resource (R) - */ - void visitResourceReference( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull Node node, - @NonNull String type, - @NonNull String name, - boolean isFramework); - } - - /** Specialized interface for detectors that scan Java class files */ - public interface ClassScanner { - /** - * Checks the given class' bytecode for issues. - * - * @param context the context of the lint check, pointing to for example - * the file - * @param classNode the root class node - */ - void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode); - - /** - * Returns the list of node types (corresponding to the constants in the - * {@link AbstractInsnNode} class) that this scanner applies to. The - * {@link #checkInstruction(ClassContext, ClassNode, MethodNode, AbstractInsnNode)} - * method will be called for each match. - * - * @return an array containing all the node types this detector should be - * called for, or null if none. - */ - @Nullable - int[] getApplicableAsmNodeTypes(); - - /** - * Process a given instruction node, and register lint issues if - * applicable. - * - * @param context the context of the lint check, pointing to for example - * the file - * @param classNode the root class node - * @param method the method node containing the call - * @param instruction the actual instruction - */ - void checkInstruction(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull AbstractInsnNode instruction); - - /** - * Return the list of method call names (in VM format, e.g. "<init>" for - * constructors, etc) for method calls this detector is interested in, - * or null. T his will be used to dispatch calls to - * {@link #checkCall(ClassContext, ClassNode, MethodNode, MethodInsnNode)} - * for only the method calls in owners that the detector is interested - * in. - * <p> - * <b>NOTE</b>: If you return non null from this method, then <b>only</b> - * {@link #checkCall(ClassContext, ClassNode, MethodNode, MethodInsnNode)} - * will be called if a suitable method is found; - * {@link #checkClass(ClassContext, ClassNode)} will not be called under - * any circumstances. - * <p> - * This makes it easy to write detectors that focus on some fixed calls, - * and allows lint to make a single pass over the bytecode over a class, - * and efficiently dispatch method calls to any detectors that are - * interested in it. Without this, each new lint check interested in a - * single method, would be doing a complete pass through all the - * bytecode instructions of the class via the - * {@link #checkClass(ClassContext, ClassNode)} method, which would make - * each newly added lint check make lint slower. Now a single dispatch - * map is used instead, and for each encountered call in the single - * dispatch, it looks up in the map which if any detectors are - * interested in the given call name, and dispatches to each one in - * turn. - * - * @return a list of applicable method names, or null. - */ - @Nullable - List<String> getApplicableCallNames(); - - /** - * Just like {@link Detector#getApplicableCallNames()}, but for the owner - * field instead. The - * {@link #checkCall(ClassContext, ClassNode, MethodNode, MethodInsnNode)} - * method will be called for all {@link MethodInsnNode} instances where the - * owner field matches any of the members returned in this node. - * <p> - * Note that if your detector provides both a name and an owner, the - * method will be called for any nodes matching either the name <b>or</b> - * the owner, not only where they match <b>both</b>. Note also that it will - * be called twice - once for the name match, and (at least once) for the owner - * match. - * - * @return a list of applicable owner names, or null. - */ - @Nullable - List<String> getApplicableCallOwners(); - - /** - * Process a given method call node, and register lint issues if - * applicable. This is similar to the - * {@link #checkInstruction(ClassContext, ClassNode, MethodNode, AbstractInsnNode)} - * method, but has the additional advantage that it is only called for known - * method names or method owners, according to - * {@link #getApplicableCallNames()} and {@link #getApplicableCallOwners()}. - * - * @param context the context of the lint check, pointing to for example - * the file - * @param classNode the root class node - * @param method the method node containing the call - * @param call the actual method call node - */ - void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode call); - } - - /** Specialized interface for detectors that scan XML files */ - public interface XmlScanner { - /** - * Visit the given document. The detector is responsible for its own iteration - * through the document. - * @param context information about the document being analyzed - * @param document the document to examine - */ - void visitDocument(@NonNull XmlContext context, @NonNull Document document); - - /** - * Visit the given element. - * @param context information about the document being analyzed - * @param element the element to examine - */ - void visitElement(@NonNull XmlContext context, @NonNull Element element); - - /** - * Visit the given element after its children have been analyzed. - * @param context information about the document being analyzed - * @param element the element to examine - */ - void visitElementAfter(@NonNull XmlContext context, @NonNull Element element); - - /** - * Visit the given attribute. - * @param context information about the document being analyzed - * @param attribute the attribute node to examine - */ - void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute); - - /** - * Returns the list of elements that this detector wants to analyze. If non - * null, this detector will be called (specifically, the - * {@link #visitElement} method) for each matching element in the document. - * <p> - * If this method returns null, and {@link #getApplicableAttributes()} also returns - * null, then the {@link #visitDocument} method will be called instead. - * - * @return a collection of elements, or null, or the special - * {@link XmlScanner#ALL} marker to indicate that every single - * element should be analyzed. - */ - @Nullable - Collection<String> getApplicableElements(); - - /** - * Returns the list of attributes that this detector wants to analyze. If non - * null, this detector will be called (specifically, the - * {@link #visitAttribute} method) for each matching attribute in the document. - * <p> - * If this method returns null, and {@link #getApplicableElements()} also returns - * null, then the {@link #visitDocument} method will be called instead. - * - * @return a collection of attributes, or null, or the special - * {@link XmlScanner#ALL} marker to indicate that every single - * attribute should be analyzed. - */ - @Nullable - Collection<String> getApplicableAttributes(); - - /** - * Special marker collection returned by {@link #getApplicableElements()} or - * {@link #getApplicableAttributes()} to indicate that the check should be - * invoked on all elements or all attributes - */ - @NonNull - List<String> ALL = new ArrayList<String>(0); // NOT Collections.EMPTY! - // We want to distinguish this from just an *empty* list returned by the caller! - } - - /** Specialized interface for detectors that scan other files */ - public interface OtherFileScanner { - /** - * Returns the set of files this scanner wants to consider. If this includes - * {@link Scope#OTHER} then all source files will be checked. Note that the - * set of files will not just include files of the indicated type, but all files - * within the relevant source folder. For example, returning {@link Scope#JAVA_FILE} - * will not just return {@code .java} files, but also other resource files such as - * {@code .html} and other files found within the Java source folders. - * <p> - * Lint will call the {@link #run(Context)}} method when the file should be checked. - * - * @return set of scopes that define the types of source files the - * detector wants to consider - */ - @NonNull - EnumSet<Scope> getApplicableFiles(); - } - - /** - * Runs the detector. This method will not be called for certain specialized - * detectors, such as {@link XmlScanner} and {@link JavaScanner}, where - * there are specialized analysis methods instead such as - * {@link XmlScanner#visitElement(XmlContext, Element)}. - * - * @param context the context describing the work to be done - */ - public void run(@NonNull Context context) { - } - - /** - * Returns true if this detector applies to the given file - * - * @param context the context to check - * @param file the file in the context to check - * @return true if this detector applies to the given context and file - */ - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return false; - } - - /** - * Analysis is about to begin, perform any setup steps. - * - * @param context the context for the check referencing the project, lint - * client, etc - */ - public void beforeCheckProject(@NonNull Context context) { - } - - /** - * Analysis has just been finished for the whole project, perform any - * cleanup or report issues that require project-wide analysis. - * - * @param context the context for the check referencing the project, lint - * client, etc - */ - public void afterCheckProject(@NonNull Context context) { - } - - /** - * Analysis is about to begin for the given library project, perform any setup steps. - * - * @param context the context for the check referencing the project, lint - * client, etc - */ - public void beforeCheckLibraryProject(@NonNull Context context) { - } - - /** - * Analysis has just been finished for the given library project, perform any - * cleanup or report issues that require library-project-wide analysis. - * - * @param context the context for the check referencing the project, lint - * client, etc - */ - public void afterCheckLibraryProject(@NonNull Context context) { - } - - /** - * Analysis is about to be performed on a specific file, perform any setup - * steps. - * <p> - * Note: When this method is called at the beginning of checking an XML - * file, the context is guaranteed to be an instance of {@link XmlContext}, - * and similarly for a Java source file, the context will be a - * {@link JavaContext} and so on. - * - * @param context the context for the check referencing the file to be - * checked, the project, etc. - */ - public void beforeCheckFile(@NonNull Context context) { - } - - /** - * Analysis has just been finished for a specific file, perform any cleanup - * or report issues found - * <p> - * Note: When this method is called at the end of checking an XML - * file, the context is guaranteed to be an instance of {@link XmlContext}, - * and similarly for a Java source file, the context will be a - * {@link JavaContext} and so on. - * - * @param context the context for the check referencing the file to be - * checked, the project, etc. - */ - public void afterCheckFile(@NonNull Context context) { - } - - /** - * Returns the expected speed of this detector - * - * @return the expected speed of this detector - */ - @NonNull - public Speed getSpeed() { - return Speed.NORMAL; - } - - // ---- Dummy implementations to make implementing XmlScanner easier: ---- - - @SuppressWarnings("javadoc") - public void visitDocument(@NonNull XmlContext context, @NonNull Document document) { - // This method must be overridden if your detector does - // not return something from getApplicableElements or - // getApplicableAttributes - assert false; - } - - @SuppressWarnings("javadoc") - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - // This method must be overridden if your detector returns - // tag names from getApplicableElements - assert false; - } - - @SuppressWarnings("javadoc") - public void visitElementAfter(@NonNull XmlContext context, @NonNull Element element) { - } - - @SuppressWarnings("javadoc") - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - // This method must be overridden if your detector returns - // attribute names from getApplicableAttributes - assert false; - } - - @SuppressWarnings("javadoc") - @Nullable - public Collection<String> getApplicableElements() { - return null; - } - - @Nullable - @SuppressWarnings("javadoc") - public Collection<String> getApplicableAttributes() { - return null; - } - - // ---- Dummy implementations to make implementing JavaScanner easier: ---- - - @Nullable @SuppressWarnings("javadoc") - public List<String> getApplicableMethodNames() { - return null; - } - - @Nullable @SuppressWarnings("javadoc") - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - return null; - } - - @Nullable @SuppressWarnings("javadoc") - public List<Class<? extends Node>> getApplicableNodeTypes() { - return null; - } - - @SuppressWarnings("javadoc") - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - } - - @SuppressWarnings("javadoc") - public boolean appliesToResourceRefs() { - return false; - } - - @SuppressWarnings("javadoc") - public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull Node node, @NonNull String type, @NonNull String name, - boolean isFramework) { - } - - // ---- Dummy implementations to make implementing a ClassScanner easier: ---- - - @SuppressWarnings("javadoc") - public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { - } - - @SuppressWarnings("javadoc") - @Nullable - public List<String> getApplicableCallNames() { - return null; - } - - @SuppressWarnings("javadoc") - @Nullable - public List<String> getApplicableCallOwners() { - return null; - } - - @SuppressWarnings("javadoc") - public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode call) { - } - - @SuppressWarnings("javadoc") - @Nullable - public int[] getApplicableAsmNodeTypes() { - return null; - } - - @SuppressWarnings("javadoc") - public void checkInstruction(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull AbstractInsnNode instruction) { - } - - // ---- Dummy implementations to make implementing an OtherFileScanner easier: ---- - - public boolean appliesToFolder(@NonNull Scope scope, @Nullable ResourceFolderType folderType) { - return false; - } - - @NonNull - public EnumSet<Scope> getApplicableFiles() { - return Scope.OTHER_SCOPE; - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Issue.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Issue.java deleted file mode 100644 index ceca9ca..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Issue.java +++ /dev/null @@ -1,553 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.Configuration; -import com.android.tools.lint.client.api.IssueRegistry; -import com.google.common.annotations.Beta; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.List; - - -/** - * An issue is a potential bug in an Android application. An issue is discovered - * by a {@link Detector}, and has an associated {@link Severity}. - * <p> - * Issues and detectors are separate classes because a detector can discover - * multiple different issues as it's analyzing code, and we want to be able to - * different severities for different issues, the ability to suppress one but - * not other issues from the same detector, and so on. - * <p/> - * <b>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.</b> - */ -@Beta -public final class Issue implements Comparable<Issue> { - private static final String HTTP_PREFIX = "http://"; //$NON-NLS-1$ - - private final String mId; - private final String mDescription; - private final String mExplanation; - private final Category mCategory; - private final int mPriority; - private final Severity mSeverity; - private String mMoreInfoUrl; - private boolean mEnabledByDefault = true; - private final EnumSet<Scope> mScope; - private List<EnumSet<Scope>> mAnalysisScopes; - private final Class<? extends Detector> mClass; - - // Use factory methods - private Issue( - @NonNull String id, - @NonNull String description, - @NonNull String explanation, - @NonNull Category category, - int priority, - @NonNull Severity severity, - @NonNull Class<? extends Detector> detectorClass, - @NonNull EnumSet<Scope> scope) { - super(); - mId = id; - mDescription = description; - mExplanation = explanation; - mCategory = category; - mPriority = priority; - mSeverity = severity; - mClass = detectorClass; - mScope = scope; - } - - /** - * Creates a new issue - * - * @param id the fixed id of the issue - * @param description the quick summary of the issue (one line) - * @param explanation a full explanation of the issue, with suggestions for - * how to fix it - * @param category the associated category, if any - * @param priority the priority, a number from 1 to 10 with 10 being most - * important/severe - * @param severity the default severity of the issue - * @param detectorClass the class of the detector to find this issue - * @param scope the scope of files required to analyze this issue - * @return a new {@link Issue} - */ - @NonNull - public static Issue create( - @NonNull String id, - @NonNull String description, - @NonNull String explanation, - @NonNull Category category, - int priority, - @NonNull Severity severity, - @NonNull Class<? extends Detector> detectorClass, - @NonNull EnumSet<Scope> scope) { - return new Issue(id, description, explanation, category, priority, severity, - detectorClass, scope); - } - - /** - * Returns the unique id of this issue. These should not change over time - * since they are used to persist the names of issues suppressed by the user - * etc. It is typically a single camel-cased word. - * - * @return the associated fixed id, never null and always unique - */ - @NonNull - public String getId() { - return mId; - } - - /** - * Briefly (one line) describes the kinds of checks performed by this rule - * - * @return a quick summary of the issue, never null - */ - @NonNull - public String getDescription() { - return mDescription; - } - - /** - * Describes the error found by this rule, e.g. - * "Buttons must define contentDescriptions". Preferably the explanation - * should also contain a description of how the problem should be solved. - * Additional info can be provided via {@link #getMoreInfo()}. - * <p> - * Note that the text may contain some simple markup, such as *'s around sentences - * for bold text, and back quotes (`) for code fragments. You can obtain - * the text without this markup by calling {@link #getExplanationAsSimpleText()}, - * and you can obtain the text as annotated HTML by calling - * {@link #getExplanationAsHtml()}. - * - * @return an explanation of the issue, never null. - */ - @NonNull - public String getExplanation() { - return mExplanation; - } - - /** - * Like {@link #getExplanation()}, but returns the text as properly escaped - * and marked up HTML, where http URLs are linked, where words with asterisks - * such as *this* are shown in bold, etc. - * - * @return the explanation of the issue, never null - */ - @NonNull - public String getExplanationAsHtml() { - return convertMarkup(mExplanation, true /* html */); - } - - /** - * Like {@link #getExplanation()}, but returns the text as properly escaped - * and marked up HTML, where http URLs are linked, where words with asterisks - * such as *this* are shown in bold, etc. - * - * @return the explanation of the issue, never null - */ - @NonNull - public String getExplanationAsSimpleText() { - return convertMarkup(mExplanation, false /* not html = text */); - } - - /** - * The primary category of the issue - * - * @return the primary category of the issue, never null - */ - @NonNull - public Category getCategory() { - return mCategory; - } - - /** - * Returns a priority, in the range 1-10, with 10 being the most severe and - * 1 the least - * - * @return a priority from 1 to 10 - */ - public int getPriority() { - return mPriority; - } - - /** - * Returns the default severity of the issues found by this detector (some - * tools may allow the user to specify custom severities for detectors). - * <p> - * Note that even though the normal way for an issue to be disabled is for - * the {@link Configuration} to return {@link Severity#IGNORE}, there is a - * {@link #isEnabledByDefault()} method which can be used to turn off issues - * by default. This is done rather than just having the severity as the only - * attribute on the issue such that an issue can be configured with an - * appropriate severity (such as {@link Severity#ERROR}) even when issues - * are disabled by default for example because they are experimental or not - * yet stable. - * - * @return the severity of the issues found by this detector - */ - @NonNull - public Severity getDefaultSeverity() { - return mSeverity; - } - - /** - * Returns a link (a URL string) to more information, or null - * - * @return a link to more information, or null - */ - @Nullable - public String getMoreInfo() { - return mMoreInfoUrl; - } - - /** - * Returns whether this issue should be enabled by default, unless the user - * has explicitly disabled it. - * - * @return true if this issue should be enabled by default - */ - public boolean isEnabledByDefault() { - return mEnabledByDefault; - } - - /** - * Returns the scope required to analyze the code to detect this issue. - * This is determined by the detectors which reports the issue. - * - * @return the required scope - */ - @NonNull - public EnumSet<Scope> getScope() { - return mScope; - } - - /** - * Sorts the detectors alphabetically by id. This is intended to make it - * convenient to store settings for detectors in a fixed order. It is not - * intended as the order to be shown to the user; for that, a tool embedding - * lint might consider the priorities, categories, severities etc of the - * various detectors. - * - * @param other the {@link Issue} to compare this issue to - */ - @Override - public int compareTo(Issue other) { - return getId().compareTo(other.getId()); - } - - /** - * Sets a more info URL string - * - * @param moreInfoUrl url string - * @return this, for constructor chaining - */ - @NonNull - public Issue setMoreInfo(@NonNull String moreInfoUrl) { - mMoreInfoUrl = moreInfoUrl; - return this; - } - - /** - * Sets whether this issue is enabled by default. - * - * @param enabledByDefault whether the issue should be enabled by default - * @return this, for constructor chaining - */ - @NonNull - public Issue setEnabledByDefault(boolean enabledByDefault) { - mEnabledByDefault = enabledByDefault; - return this; - } - - /** - * Returns the sets of scopes required to analyze this issue, or null if all - * scopes named by {@link Issue#getScope()} are necessary. Note that only - * <b>one</b> match out of this collection is required, not all, and that - * the scope set returned by {@link #getScope()} does not have to be returned - * by this method, but is always implied to be included. - * <p> - * The scopes returned by {@link Issue#getScope()} list all the various - * scopes that are <b>affected</b> by this issue, meaning the detector - * should consider it. Frequently, the detector must analyze all these - * scopes in order to properly decide whether an issue is found. For - * example, the unused resource detector needs to consider both the XML - * resource files and the Java source files in order to decide if a resource - * is unused. If it analyzes just the Java files for example, it might - * incorrectly conclude that a resource is unused because it did not - * discover a resource reference in an XML file. - * <p> - * However, there are other issues where the issue can occur in a variety of - * files, but the detector can consider each in isolation. For example, the - * API checker is affected by both XML files and Java class files (detecting - * both layout constructor references in XML layout files as well as code - * references in .class files). It doesn't have to analyze both; it is - * capable of incrementally analyzing just an XML file, or just a class - * file, without considering the other. - * <p> - * The required scope list provides a list of scope sets that can be used to - * analyze this issue. For each scope set, all the scopes must be matched by - * the incremental analysis, but any one of the scope sets can be analyzed - * in isolation. - * <p> - * The required scope list is not required to include the full scope set - * returned by {@link #getScope()}; that set is always assumed to be - * included. - * <p> - * NOTE: You would normally call {@link #isAdequate(EnumSet)} rather - * than calling this method directly. - * - * @return a list of required scopes, or null. - */ - @Nullable - public Collection<EnumSet<Scope>> getAnalysisScopes() { - return mAnalysisScopes; - } - - /** - * Sets the collection of scopes that are allowed to be analyzed independently. - * See the {@link #getAnalysisScopes()} method for a full explanation. - * Note that you usually want to just call {@link #addAnalysisScope(EnumSet)} - * instead of constructing a list up front and passing it in here. This - * method exists primarily such that commonly used share sets of analysis - * scopes can be reused and set directly. - * - * @param required the collection of scopes - * @return this, for constructor chaining - */ - @NonNull - public Issue setAnalysisScopes(@Nullable List<EnumSet<Scope>> required) { - mAnalysisScopes = required; - - return this; - } - - /** - * Returns true if the given scope is adequate for analyzing this issue. - * This looks through the analysis scopes (see - * {@link #addAnalysisScope(EnumSet)}) and if the scope passed in fully - * covers at least one of them, or if it covers the scope of the issue - * itself (see {@link #getScope()}, which should be a superset of all the - * analysis scopes) returns true. - * <p> - * The scope set returned by {@link Issue#getScope()} lists all the various - * scopes that are <b>affected</b> by this issue, meaning the detector - * should consider it. Frequently, the detector must analyze all these - * scopes in order to properly decide whether an issue is found. For - * example, the unused resource detector needs to consider both the XML - * resource files and the Java source files in order to decide if a resource - * is unused. If it analyzes just the Java files for example, it might - * incorrectly conclude that a resource is unused because it did not - * discover a resource reference in an XML file. - * <p> - * However, there are other issues where the issue can occur in a variety of - * files, but the detector can consider each in isolation. For example, the - * API checker is affected by both XML files and Java class files (detecting - * both layout constructor references in XML layout files as well as code - * references in .class files). It doesn't have to analyze both; it is - * capable of incrementally analyzing just an XML file, or just a class - * file, without considering the other. - * <p> - * An issue can register additional scope sets that can are adequate - * for analyzing the issue, by calling {@link #addAnalysisScope(EnumSet)}. - * This method returns true if the given scope matches one or more analysis - * scope, or the overall scope. - * - * @param scope the scope available for analysis - * @return true if this issue can be analyzed with the given available scope - */ - public boolean isAdequate(@NonNull EnumSet<Scope> scope) { - if (scope.containsAll(mScope)) { - return true; - } - - if (mAnalysisScopes != null) { - for (EnumSet<Scope> analysisScope : mAnalysisScopes) { - if (mScope.containsAll(analysisScope)) { - return true; - } - } - } - - if (this == IssueRegistry.LINT_ERROR || this == IssueRegistry.PARSER_ERROR) { - return true; - } - - return false; - } - - /** - * Adds a scope set that can be analyzed independently to uncover this issue. - * See the {@link #getAnalysisScopes()} method for a full explanation. - * Note that the {@link #getScope()} does not have to be added here; it is - * always considered an analysis scope. - * - * @param scope the additional scope which can analyze this issue independently - * @return this, for constructor chaining - */ - public Issue addAnalysisScope(@Nullable EnumSet<Scope> scope) { - if (mAnalysisScopes == null) { - mAnalysisScopes = new ArrayList<EnumSet<Scope>>(2); - } - mAnalysisScopes.add(scope); - - return this; - } - - /** - * Returns the class of the detector to use to find this issue - * - * @return the class of the detector to use to find this issue - */ - @NonNull - public Class<? extends Detector> getDetectorClass() { - return mClass; - } - - @Override - public String toString() { - return mId; - } - - /** - * Converts the given markup text to HTML or text, depending on the. - * <p> - * This will recognize the following formatting conventions: - * <ul> - * <li>HTTP urls (http://...) - * <li>Sentences immediately surrounded by * will be shown as bold. - * <li>Sentences immediately surrounded by ` will be shown using monospace - * fonts - * </ul> - * Furthermore, newlines are converted to br's when converting newlines. - * Note: It does not insert {@code <html>} tags around the fragment for HTML output. - * <p> - * TODO: Consider switching to the restructured text format - - * http://docutils.sourceforge.net/docs/user/rst/quickstart.html - * - * @param text the text to be formatted - * @param html whether to convert into HTML or text - * @return the corresponding HTML or text properly formatted - */ - @NonNull - public static String convertMarkup(@NonNull String text, boolean html) { - StringBuilder sb = new StringBuilder(3 * text.length() / 2); - - char prev = 0; - int flushIndex = 0; - int n = text.length(); - for (int i = 0; i < n; i++) { - char c = text.charAt(i); - if ((c == '*' || c == '`' && i < n - 1)) { - // Scout ahead for range end - if (!Character.isLetterOrDigit(prev) - && !Character.isWhitespace(text.charAt(i + 1))) { - // Found * or ~ immediately before a letter, and not in the middle of a word - // Find end - int end = text.indexOf(c, i + 1); - if (end != -1 && (end == n - 1 || !Character.isLetter(text.charAt(end + 1)))) { - if (i > flushIndex) { - appendEscapedText(sb, text, html, flushIndex, i); - } - if (html) { - String tag = c == '*' ? "b" : "code"; //$NON-NLS-1$ //$NON-NLS-2$ - sb.append('<').append(tag).append('>'); - appendEscapedText(sb, text, html, i + 1, end); - sb.append('<').append('/').append(tag).append('>'); - } else { - appendEscapedText(sb, text, html, i + 1, end); - } - flushIndex = end + 1; - i = flushIndex - 1; // -1: account for the i++ in the loop - } - } - } else if (html && c == 'h' && i < n - 1 && text.charAt(i + 1) == 't' - && text.startsWith(HTTP_PREFIX, i) && !Character.isLetterOrDigit(prev)) { - // Find url end - int end = i + HTTP_PREFIX.length(); - while (end < n) { - char d = text.charAt(end); - if (Character.isWhitespace(d)) { - break; - } - end++; - } - char last = text.charAt(end - 1); - if (last == '.' || last == ')' || last == '!') { - end--; - } - if (end > i + HTTP_PREFIX.length()) { - if (i > flushIndex) { - appendEscapedText(sb, text, html, flushIndex, i); - } - - String url = text.substring(i, end); - sb.append("<a href=\""); //$NON-NLS-1$ - sb.append(url); - sb.append('"').append('>'); - sb.append(url); - sb.append("</a>"); //$NON-NLS-1$ - - flushIndex = end; - i = flushIndex - 1; // -1: account for the i++ in the loop - } - } - prev = c; - } - - if (flushIndex < n) { - appendEscapedText(sb, text, html, flushIndex, n); - } - - return sb.toString(); - } - - private static void appendEscapedText(StringBuilder sb, String text, boolean html, - int start, int end) { - if (html) { - for (int i = start; i < end; i++) { - char c = text.charAt(i); - if (c == '<') { - sb.append("<"); //$NON-NLS-1$ - } else if (c == '&') { - sb.append("&"); //$NON-NLS-1$ - } else if (c == '\n') { - sb.append("<br/>\n"); - } else { - if (c > 255) { - sb.append("&#"); //$NON-NLS-1$ - sb.append(Integer.toString(c)); - sb.append(';'); - } else { - sb.append(c); - } - } - } - } else { - for (int i = start; i < end; i++) { - char c = text.charAt(i); - sb.append(c); - } - } - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java deleted file mode 100644 index d91f423..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.IJavaParser; -import com.android.tools.lint.client.api.LintDriver; - -import java.io.File; - -import lombok.ast.ConstructorDeclaration; -import lombok.ast.MethodDeclaration; -import lombok.ast.Node; - -/** - * A {@link Context} used when checking Java files. - * <p/> - * <b>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.</b> - */ -public class JavaContext extends Context { - /** The parse tree */ - public Node compilationUnit; - /** The parser which produced the parse tree */ - public IJavaParser parser; - - /** - * Constructs a {@link JavaContext} for running lint on the given file, with - * the given scope, in the given project reporting errors to the given - * client. - * - * @param driver the driver running through the checks - * @param project the project to run lint on which contains the given file - * @param main the main project if this project is a library project, or - * null if this is not a library project. The main project is - * the root project of all library projects, not necessarily the - * directly including project. - * @param file the file to be analyzed - */ - public JavaContext( - @NonNull LintDriver driver, - @NonNull Project project, - @Nullable Project main, - @NonNull File file) { - super(driver, project, main, file); - } - - /** - * Returns a location for the given node - * - * @param node the AST node to get a location for - * @return a location for the given node - */ - @NonNull - public Location getLocation(@NonNull Node node) { - if (parser != null) { - return parser.getLocation(this, node); - } - - return new Location(file, null, null); - } - - @Override - public void report(@NonNull Issue issue, @Nullable Location location, - @NonNull String message, @Nullable Object data) { - if (mDriver.isSuppressed(issue, compilationUnit)) { - return; - } - super.report(issue, location, message, data); - } - - /** - * Reports an issue applicable to a given AST node. The AST node is used as the - * scope to check for suppress lint annotations. - * - * @param issue the issue to report - * @param scope the AST node scope the error applies to. The lint infrastructure - * will check whether there are suppress annotations on this node (or its enclosing - * nodes) and if so suppress the warning without involving the client. - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - public void report( - @NonNull Issue issue, - @Nullable Node scope, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - if (scope != null && mDriver.isSuppressed(issue, scope)) { - return; - } - super.report(issue, location, message, data); - } - - - @Nullable - public static Node findSurroundingMethod(Node scope) { - while (scope != null) { - Class<? extends Node> type = scope.getClass(); - // The Lombok AST uses a flat hierarchy of node type implementation classes - // so no need to do instanceof stuff here. - if (type == MethodDeclaration.class || type == ConstructorDeclaration.class) { - return scope; - } - - scope = scope.getParent(); - } - - return null; - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/LayoutDetector.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/LayoutDetector.java deleted file mode 100644 index b24c1a9..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/LayoutDetector.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.detector.api; - -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ATTR_PADDING; -import static com.android.SdkConstants.ATTR_PADDING_BOTTOM; -import static com.android.SdkConstants.ATTR_PADDING_LEFT; -import static com.android.SdkConstants.ATTR_PADDING_RIGHT; -import static com.android.SdkConstants.ATTR_PADDING_TOP; -import static com.android.SdkConstants.VALUE_FILL_PARENT; -import static com.android.SdkConstants.VALUE_MATCH_PARENT; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.google.common.annotations.Beta; - -import org.w3c.dom.Element; - -/** - * Abstract class specifically intended for layout detectors which provides some - * common utility methods shared by layout detectors. - * <p/> - * <b>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.</b> - */ -@Beta -public abstract class LayoutDetector extends ResourceXmlDetector { - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.LAYOUT; - } - - private static boolean isFillParent(@NonNull Element element, @NonNull String dimension) { - String width = element.getAttributeNS(ANDROID_URI, dimension); - return width.equals(VALUE_MATCH_PARENT) || width.equals(VALUE_FILL_PARENT); - } - - protected static boolean isWidthFillParent(@NonNull Element element) { - return isFillParent(element, ATTR_LAYOUT_WIDTH); - } - - protected static boolean isHeightFillParent(@NonNull Element element) { - return isFillParent(element, ATTR_LAYOUT_HEIGHT); - } - - protected boolean hasPadding(@NonNull Element root) { - return root.hasAttributeNS(ANDROID_URI, ATTR_PADDING) - || root.hasAttributeNS(ANDROID_URI, ATTR_PADDING_LEFT) - || root.hasAttributeNS(ANDROID_URI, ATTR_PADDING_RIGHT) - || root.hasAttributeNS(ANDROID_URI, ATTR_PADDING_TOP) - || root.hasAttributeNS(ANDROID_URI, ATTR_PADDING_BOTTOM); - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/LintUtils.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/LintUtils.java deleted file mode 100644 index 9dec569..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/LintUtils.java +++ /dev/null @@ -1,798 +0,0 @@ -/* - * 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.detector.api; - -import static com.android.SdkConstants.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.BIN_FOLDER; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.ID_PREFIX; -import static com.android.SdkConstants.NEW_ID_PREFIX; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.FolderTypeRelationship; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; -import com.android.tools.lint.client.api.LintClient; -import com.android.utils.PositionXmlParser; -import com.google.common.annotations.Beta; -import com.google.common.base.Splitter; -import com.google.common.collect.Iterables; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; - -import lombok.ast.ImportDeclaration; - - -/** - * Useful utility methods related to lint. - * <p> - * <b>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.</b> - */ -@Beta -public class LintUtils { - // Utility class, do not instantiate - private LintUtils() { - } - - /** - * Format a list of strings, and cut of the list at {@code maxItems} if the - * number of items are greater. - * - * @param strings the list of strings to print out as a comma separated list - * @param maxItems the maximum number of items to print - * @return a comma separated list - */ - @NonNull - public static String formatList(@NonNull List<String> strings, int maxItems) { - StringBuilder sb = new StringBuilder(20 * strings.size()); - - for (int i = 0, n = strings.size(); i < n; i++) { - if (sb.length() > 0) { - sb.append(", "); //$NON-NLS-1$ - } - sb.append(strings.get(i)); - - if (maxItems > 0 && i == maxItems - 1 && n > maxItems) { - sb.append(String.format("... (%1$d more)", n - i - 1)); - break; - } - } - - return sb.toString(); - } - - /** - * Determine if the given type corresponds to a resource that has a unique - * file - * - * @param type the resource type to check - * @return true if the given type corresponds to a file-type resource - */ - public static boolean isFileBasedResourceType(@NonNull ResourceType type) { - List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type); - for (ResourceFolderType folderType : folderTypes) { - if (folderType != ResourceFolderType.VALUES) { - if (type == ResourceType.ID) { - return false; - } - return true; - } - } - return false; - } - - /** - * Returns true if the given file represents an XML file - * - * @param file the file to be checked - * @return true if the given file is an xml file - */ - public static boolean isXmlFile(@NonNull File file) { - String string = file.getName(); - return string.regionMatches(true, string.length() - DOT_XML.length(), - DOT_XML, 0, DOT_XML.length()); - } - - /** - * Case insensitive ends with - * - * @param string the string to be tested whether it ends with the given - * suffix - * @param suffix the suffix to check - * @return true if {@code string} ends with {@code suffix}, - * case-insensitively. - */ - public static boolean endsWith(@NonNull String string, @NonNull String suffix) { - return string.regionMatches(true /* ignoreCase */, string.length() - suffix.length(), - suffix, 0, suffix.length()); - } - - /** - * Case insensitive starts with - * - * @param string the string to be tested whether it starts with the given prefix - * @param prefix the prefix to check - * @param offset the offset to start checking with - * @return true if {@code string} starts with {@code prefix}, - * case-insensitively. - */ - public static boolean startsWith(@NonNull String string, @NonNull String prefix, int offset) { - return string.regionMatches(true /* ignoreCase */, offset, prefix, 0, prefix.length()); - } - - /** - * Returns the basename of the given filename, unless it's a dot-file such as ".svn". - * - * @param fileName the file name to extract the basename from - * @return the basename (the filename without the file extension) - */ - public static String getBaseName(@NonNull String fileName) { - int extension = fileName.indexOf('.'); - if (extension > 0) { - return fileName.substring(0, extension); - } else { - return fileName; - } - } - - /** - * Returns the children elements of the given node - * - * @param node the parent node - * @return a list of element children, never null - */ - @NonNull - public static List<Element> getChildren(@NonNull Node node) { - NodeList childNodes = node.getChildNodes(); - List<Element> children = new ArrayList<Element>(childNodes.getLength()); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE) { - children.add((Element) child); - } - } - - return children; - } - - /** - * Returns the <b>number</b> of children of the given node - * - * @param node the parent node - * @return the count of element children - */ - public static int getChildCount(@NonNull Node node) { - NodeList childNodes = node.getChildNodes(); - int childCount = 0; - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE) { - childCount++; - } - } - - return childCount; - } - - /** - * Returns true if the given element is the root element of its document - * - * @param element the element to test - * @return true if the element is the root element - */ - public static boolean isRootElement(Element element) { - return element == element.getOwnerDocument().getDocumentElement(); - } - - /** - * Returns the given id without an {@code @id/} or {@code @+id} prefix - * - * @param id the id to strip - * @return the stripped id, never null - */ - @NonNull - public static String stripIdPrefix(@Nullable String id) { - if (id == null) { - return ""; - } else if (id.startsWith(NEW_ID_PREFIX)) { - return id.substring(NEW_ID_PREFIX.length()); - } else if (id.startsWith(ID_PREFIX)) { - return id.substring(ID_PREFIX.length()); - } - - return id; - } - - /** - * Returns true if the given two id references match. This is similar to - * String equality, but it also considers "{@code @+id/foo == @id/foo}. - * - * @param id1 the first id to compare - * @param id2 the second id to compare - * @return true if the two id references refer to the same id - */ - public static boolean idReferencesMatch(String id1, String id2) { - if (id1.startsWith(NEW_ID_PREFIX)) { - if (id2.startsWith(NEW_ID_PREFIX)) { - return id1.equals(id2); - } else { - assert id2.startsWith(ID_PREFIX); - return ((id1.length() - id2.length()) - == (NEW_ID_PREFIX.length() - ID_PREFIX.length())) - && id1.regionMatches(NEW_ID_PREFIX.length(), id2, - ID_PREFIX.length(), - id2.length() - ID_PREFIX.length()); - } - } else { - assert id1.startsWith(ID_PREFIX); - if (id2.startsWith(ID_PREFIX)) { - return id1.equals(id2); - } else { - assert id2.startsWith(NEW_ID_PREFIX); - return (id2.length() - id1.length() - == (NEW_ID_PREFIX.length() - ID_PREFIX.length())) - && id2.regionMatches(NEW_ID_PREFIX.length(), id1, - ID_PREFIX.length(), - id1.length() - ID_PREFIX.length()); - } - } - } - - /** - * Computes the edit distance (number of insertions, deletions or substitutions - * to edit one string into the other) between two strings. In particular, - * this will compute the Levenshtein distance. - * <p> - * See http://en.wikipedia.org/wiki/Levenshtein_distance for details. - * - * @param s the first string to compare - * @param t the second string to compare - * @return the edit distance between the two strings - */ - public static int editDistance(@NonNull String s, @NonNull String t) { - int m = s.length(); - int n = t.length(); - int[][] d = new int[m + 1][n + 1]; - for (int i = 0; i <= m; i++) { - d[i][0] = i; - } - for (int j = 0; j <= n; j++) { - d[0][j] = j; - } - for (int j = 1; j <= n; j++) { - for (int i = 1; i <= m; i++) { - if (s.charAt(i - 1) == t.charAt(j - 1)) { - d[i][j] = d[i - 1][j - 1]; - } else { - int deletion = d[i - 1][j] + 1; - int insertion = d[i][j - 1] + 1; - int substitution = d[i - 1][j - 1] + 1; - d[i][j] = Math.min(deletion, Math.min(insertion, substitution)); - } - } - } - - return d[m][n]; - } - - /** - * Returns true if assertions are enabled - * - * @return true if assertions are enabled - */ - @SuppressWarnings("all") - public static boolean assertionsEnabled() { - boolean assertionsEnabled = false; - assert assertionsEnabled = true; // Intentional side-effect - return assertionsEnabled; - } - - /** - * Returns the layout resource name for the given layout file - * - * @param layoutFile the file pointing to the layout - * @return the layout resource name, not including the {@code @layout} - * prefix - */ - public static String getLayoutName(File layoutFile) { - String name = layoutFile.getName(); - int dotIndex = name.indexOf('.'); - if (dotIndex != -1) { - name = name.substring(0, dotIndex); - } - return name; - } - - /** - * Splits the given path into its individual parts, attempting to be - * tolerant about path separators (: or ;). It can handle possibly ambiguous - * paths, such as {@code c:\foo\bar:\other}, though of course these are to - * be avoided if possible. - * - * @param path the path variable to split, which can use both : and ; as - * path separators. - * @return the individual path components as an Iterable of strings - */ - public static Iterable<String> splitPath(String path) { - if (path.indexOf(';') != -1) { - return Splitter.on(';').omitEmptyStrings().trimResults().split(path); - } - - List<String> combined = new ArrayList<String>(); - Iterables.addAll(combined, Splitter.on(':').omitEmptyStrings().trimResults().split(path)); - for (int i = 0, n = combined.size(); i < n; i++) { - String p = combined.get(i); - if (p.length() == 1 && i < n - 1 && Character.isLetter(p.charAt(0)) - // Technically, Windows paths do not have to have a \ after the :, - // which means it would be using the current directory on that drive, - // but that's unlikely to be the case in a path since it would have - // unpredictable results - && !combined.get(i+1).isEmpty() && combined.get(i+1).charAt(0) == '\\') { - combined.set(i, p + ':' + combined.get(i+1)); - combined.remove(i+1); - n--; - continue; - } - } - - return combined; - } - - /** - * Computes the shared parent among a set of files (which may be null). - * - * @param files the set of files to be checked - * @return the closest common ancestor file, or null if none was found - */ - @Nullable - public static File getCommonParent(@NonNull List<File> files) { - int fileCount = files.size(); - if (fileCount == 0) { - return null; - } else if (fileCount == 1) { - return files.get(0); - } else if (fileCount == 2) { - return getCommonParent(files.get(0), files.get(1)); - } else { - File common = files.get(0); - for (int i = 1; i < fileCount; i++) { - common = getCommonParent(common, files.get(i)); - if (common == null) { - return null; - } - } - - return common; - } - } - - /** - * Computes the closest common parent path between two files. - * - * @param file1 the first file to be compared - * @param file2 the second file to be compared - * @return the closest common ancestor file, or null if the two files have - * no common parent - */ - @Nullable - public static File getCommonParent(@NonNull File file1, @NonNull File file2) { - if (file1.equals(file2)) { - return file1; - } else if (file1.getPath().startsWith(file2.getPath())) { - return file2; - } else if (file2.getPath().startsWith(file1.getPath())) { - return file1; - } else { - // Dumb and simple implementation - File first = file1.getParentFile(); - while (first != null) { - File second = file2.getParentFile(); - while (second != null) { - if (first.equals(second)) { - return first; - } - second = second.getParentFile(); - } - - first = first.getParentFile(); - } - } - return null; - } - - private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$ - private static final String UTF_16 = "UTF_16"; //$NON-NLS-1$ - private static final String UTF_16LE = "UTF_16LE"; //$NON-NLS-1$ - - /** - * Returns the encoded String for the given file. This is usually the - * same as {@code Files.toString(file, Charsets.UTF8}, but if there's a UTF byte order mark - * (for UTF8, UTF_16 or UTF_16LE), use that instead. - * - * @param client the client to use for I/O operations - * @param file the file to read from - * @return the string - * @throws IOException if the file cannot be read properly - */ - @NonNull - public static String getEncodedString( - @NonNull LintClient client, - @NonNull File file) throws IOException { - byte[] bytes = client.readBytes(file); - if (endsWith(file.getName(), DOT_XML)) { - return PositionXmlParser.getXmlString(bytes); - } - - return getEncodedString(bytes); - } - - /** - * Returns the String corresponding to the given data. This is usually the - * same as {@code new String(data)}, but if there's a UTF byte order mark - * (for UTF8, UTF_16 or UTF_16LE), use that instead. - * <p> - * NOTE: For XML files, there is the additional complication that there - * could be a {@code encoding=} attribute in the prologue. For those files, - * use {@link PositionXmlParser#getXmlString(byte[])} instead. - * - * @param data the byte array to construct the string from - * @return the string - */ - @NonNull - public static String getEncodedString(@Nullable byte[] data) { - if (data == null) { - return ""; - } - - int offset = 0; - String defaultCharset = UTF_8; - String charset = null; - // Look for the byte order mark, to see if we need to remove bytes from - // the input stream (and to determine whether files are big endian or little endian) etc - // for files which do not specify the encoding. - // See http://unicode.org/faq/utf_bom.html#BOM for more. - if (data.length > 4) { - if (data[0] == (byte)0xef && data[1] == (byte)0xbb && data[2] == (byte)0xbf) { - // UTF-8 - defaultCharset = charset = UTF_8; - offset += 3; - } else if (data[0] == (byte)0xfe && data[1] == (byte)0xff) { - // UTF-16, big-endian - defaultCharset = charset = UTF_16; - offset += 2; - } else if (data[0] == (byte)0x0 && data[1] == (byte)0x0 - && data[2] == (byte)0xfe && data[3] == (byte)0xff) { - // UTF-32, big-endian - defaultCharset = charset = "UTF_32"; //$NON-NLS-1$ - offset += 4; - } else if (data[0] == (byte)0xff && data[1] == (byte)0xfe - && data[2] == (byte)0x0 && data[3] == (byte)0x0) { - // UTF-32, little-endian. We must check for this *before* looking for - // UTF_16LE since UTF_32LE has the same prefix! - defaultCharset = charset = "UTF_32LE"; //$NON-NLS-1$ - offset += 4; - } else if (data[0] == (byte)0xff && data[1] == (byte)0xfe) { - // UTF-16, little-endian - defaultCharset = charset = UTF_16LE; - offset += 2; - } - } - int length = data.length - offset; - - // Guess encoding by searching for an encoding= entry in the first line. - boolean seenOddZero = false; - boolean seenEvenZero = false; - for (int lineEnd = offset; lineEnd < data.length; lineEnd++) { - if (data[lineEnd] == 0) { - if ((lineEnd - offset) % 2 == 0) { - seenEvenZero = true; - } else { - seenOddZero = true; - } - } else if (data[lineEnd] == '\n' || data[lineEnd] == '\r') { - break; - } - } - - if (charset == null) { - charset = seenOddZero ? UTF_16LE : seenEvenZero ? UTF_16 : UTF_8; - } - - String text = null; - try { - text = new String(data, offset, length, charset); - } catch (UnsupportedEncodingException e) { - try { - if (charset != defaultCharset) { - text = new String(data, offset, length, defaultCharset); - } - } catch (UnsupportedEncodingException u) { - // Just use the default encoding below - } - } - if (text == null) { - text = new String(data, offset, length); - } - return text; - } - - /** - * Returns true if the given class node represents a static inner class. - * - * @param classNode the inner class to be checked - * @return true if the class node represents an inner class that is static - */ - public static boolean isStaticInnerClass(@NonNull ClassNode classNode) { - // Note: We can't just filter out static inner classes like this: - // (classNode.access & Opcodes.ACC_STATIC) != 0 - // because the static flag only appears on methods and fields in the class - // file. Instead, look for the synthetic this pointer. - - @SuppressWarnings("rawtypes") // ASM API - List fieldList = classNode.fields; - for (Object f : fieldList) { - FieldNode field = (FieldNode) f; - if (field.name.startsWith("this$") && (field.access & Opcodes.ACC_SYNTHETIC) != 0) { - return false; - } - } - - return true; - } - - /** - * Returns the previous opcode prior to the given node, ignoring label and - * line number nodes - * - * @param node the node to look up the previous opcode for - * @return the previous opcode, or {@link Opcodes#NOP} if no previous node - * was found - */ - public static int getPrevOpcode(@NonNull AbstractInsnNode node) { - AbstractInsnNode prev = getPrevInstruction(node); - if (prev != null) { - return prev.getOpcode(); - } else { - return Opcodes.NOP; - } - } - - /** - * Returns the previous instruction prior to the given node, ignoring label - * and line number nodes. - * - * @param node the node to look up the previous instruction for - * @return the previous instruction, or null if no previous node was found - */ - @Nullable - public static AbstractInsnNode getPrevInstruction(@NonNull AbstractInsnNode node) { - AbstractInsnNode prev = node; - while (true) { - prev = prev.getPrevious(); - if (prev == null) { - return null; - } else { - int type = prev.getType(); - if (type != AbstractInsnNode.LINE && type != AbstractInsnNode.LABEL - && type != AbstractInsnNode.FRAME) { - return prev; - } - } - } - } - - /** - * Returns the next opcode after to the given node, ignoring label and line - * number nodes - * - * @param node the node to look up the next opcode for - * @return the next opcode, or {@link Opcodes#NOP} if no next node was found - */ - public static int getNextOpcode(@NonNull AbstractInsnNode node) { - AbstractInsnNode next = getNextInstruction(node); - if (next != null) { - return next.getOpcode(); - } else { - return Opcodes.NOP; - } - } - - /** - * Returns the next instruction after to the given node, ignoring label and - * line number nodes. - * - * @param node the node to look up the next node for - * @return the next instruction, or null if no next node was found - */ - @Nullable - public static AbstractInsnNode getNextInstruction(@NonNull AbstractInsnNode node) { - AbstractInsnNode next = node; - while (true) { - next = next.getNext(); - if (next == null) { - return null; - } else { - int type = next.getType(); - if (type != AbstractInsnNode.LINE && type != AbstractInsnNode.LABEL - && type != AbstractInsnNode.FRAME) { - return next; - } - } - } - } - - /** - * Returns true if the given directory represents an Android project - * directory. Note: This doesn't necessarily mean it's an Eclipse directory, - * only that it looks like it contains a logical Android project -- one - * including a manifest file, a resource folder, etc. - * - * @param dir the directory to check - * @return true if the directory looks like an Android project - */ - public static boolean isProjectDir(@NonNull File dir) { - boolean hasManifest = new File(dir, ANDROID_MANIFEST_XML).exists(); - if (hasManifest) { - // Special case: the bin/ folder can also contain a copy of the - // manifest file, but this is *not* a project directory - if (dir.getName().equals(BIN_FOLDER)) { - // ...unless of course it just *happens* to be a project named bin, in - // which case we peek at its parent to see if this is the case - dir = dir.getParentFile(); - if (dir != null && isProjectDir(dir)) { - // Yes, it's a bin/ directory inside a real project: ignore this dir - return false; - } - } - } else if (Project.isAospFrameworksProject(dir)) { - // Hardcoded AOSP support for the frameworks project - return true; - } - - return hasManifest; - } - - /** - * Look up the locale and region from the given parent folder name and - * return it as a combined string, such as "en", "en-rUS", etc, or null if - * no language is specified. - * - * @param folderName the folder name - * @return the locale+region string or null - */ - @Nullable - public static String getLocaleAndRegion(@NonNull String folderName) { - if (folderName.equals("values")) { //$NON-NLS-1$ - return null; - } - - String locale = null; - - for (String qualifier : Splitter.on('-').split(folderName)) { - int qualifierLength = qualifier.length(); - if (qualifierLength == 2) { - char first = qualifier.charAt(0); - char second = qualifier.charAt(1); - if (first >= 'a' && first <= 'z' && second >= 'a' && second <= 'z') { - locale = qualifier; - } - } else if (qualifierLength == 3 && qualifier.charAt(0) == 'r' && locale != null) { - char first = qualifier.charAt(1); - char second = qualifier.charAt(2); - if (first >= 'A' && first <= 'Z' && second >= 'A' && second <= 'Z') { - return locale + '-' + qualifier; - } - break; - } - } - - return locale; - } - - /** - * Returns true if the given class (specified by a fully qualified class - * name) name is imported in the given compilation unit either through a fully qualified - * import or by a wildcard import. - * - * @param compilationUnit the compilation unit - * @param fullyQualifiedName the fully qualified class name - * @return true if the given imported name refers to the given fully - * qualified name - */ - public static boolean isImported( - @NonNull lombok.ast.Node compilationUnit, - @NonNull String fullyQualifiedName) { - int dotIndex = fullyQualifiedName.lastIndexOf('.'); - int dotLength = fullyQualifiedName.length() - dotIndex; - - boolean imported = false; - for (lombok.ast.Node rootNode : compilationUnit.getChildren()) { - if (rootNode instanceof ImportDeclaration) { - ImportDeclaration importDeclaration = (ImportDeclaration) rootNode; - String fqn = importDeclaration.asFullyQualifiedName(); - if (fqn.equals(fullyQualifiedName)) { - return true; - } else if (fullyQualifiedName.regionMatches(dotIndex, fqn, - fqn.length() - dotLength, dotLength)) { - // This import is importing the class name using some other prefix, so there - // fully qualified class name cannot be imported under that name - return false; - } else if (importDeclaration.astStarImport() - && fqn.regionMatches(0, fqn, 0, dotIndex + 1)) { - imported = true; - // but don't break -- keep searching in case there's a non-wildcard - // import of the specific class name, e.g. if we're looking for - // android.content.SharedPreferences.Editor, don't match on the following: - // import android.content.SharedPreferences.*; - // import foo.bar.Editor; - } - } - } - - return imported; - } - - /** - * Returns the applicable build code (for - * {@code android.os.Build.VERSION_CODES}) for the corresponding API level, - * or null if it's unknown. - * - * @param api the API level to look up a version code for - * @return the corresponding build code field name, or null - */ - @Nullable - public static String getBuildCode(int api) { - // See http://developer.android.com/reference/android/os/Build.VERSION_CODES.html - switch (api) { - case 1: return "BASE"; //$NON-NLS-1$ - case 2: return "BASE_1_1"; //$NON-NLS-1$ - case 3: return "CUPCAKE"; //$NON-NLS-1$ - case 4: return "DONUT"; //$NON-NLS-1$ - case 5: return "ECLAIR"; //$NON-NLS-1$ - case 6: return "ECLAIR_0_1"; //$NON-NLS-1$ - case 7: return "ECLAIR_MR1"; //$NON-NLS-1$ - case 8: return "FROYO"; //$NON-NLS-1$ - case 9: return "GINGERBREAD"; //$NON-NLS-1$ - case 10: return "GINGERBREAD_MR1"; //$NON-NLS-1$ - case 11: return "HONEYCOMB"; //$NON-NLS-1$ - case 12: return "HONEYCOMB_MR1"; //$NON-NLS-1$ - case 13: return "HONEYCOMB_MR2"; //$NON-NLS-1$ - case 14: return "ICE_CREAM_SANDWICH"; //$NON-NLS-1$ - case 15: return "ICE_CREAM_SANDWICH_MR1"; //$NON-NLS-1$ - case 16: return "JELLY_BEAN"; //$NON-NLS-1$ - case 17: return "JELLY_BEAN_MR1"; //$NON-NLS-1$ - // If you add more versions here, also update AdtUtils#getAndroidName and - // SdkConstants#HIGHEST_KNOWN_API - } - - return null; - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Location.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Location.java deleted file mode 100644 index 34116d0..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Location.java +++ /dev/null @@ -1,722 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.google.common.annotations.Beta; - -import java.io.File; - -/** - * Location information for a warning - * <p/> - * <b>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.</b> - */ -@Beta -public class Location { - private static final String SUPER_KEYWORD = "super"; //$NON-NLS-1$ - - private final File mFile; - private final Position mStart; - private final Position mEnd; - private String mMessage; - private Location mSecondary; - private Object mClientData; - - /** - * (Private constructor, use one of the factory methods - * {@link Location#create(File)}, - * {@link Location#create(File, Position, Position)}, or - * {@link Location#create(File, String, int, int)}. - * <p> - * Constructs a new location range for the given file, from start to end. If - * the length of the range is not known, end may be null. - * - * @param file the associated file (but see the documentation for - * {@link #getFile()} for more information on what the file - * represents) - * @param start the starting position, or null - * @param end the ending position, or null - */ - protected Location(@NonNull File file, @Nullable Position start, @Nullable Position end) { - super(); - mFile = file; - mStart = start; - mEnd = end; - } - - /** - * Returns the file containing the warning. Note that the file *itself* may - * not yet contain the error. When editing a file in the IDE for example, - * the tool could generate warnings in the background even before the - * document is saved. However, the file is used as a identifying token for - * the document being edited, and the IDE integration can map this back to - * error locations in the editor source code. - * - * @return the file handle for the location - */ - @NonNull - public File getFile() { - return mFile; - } - - /** - * The start position of the range - * - * @return the start position of the range, or null - */ - @Nullable - public Position getStart() { - return mStart; - } - - /** - * The end position of the range - * - * @return the start position of the range, may be null for an empty range - */ - @Nullable - public Position getEnd() { - return mEnd; - } - - /** - * Returns a secondary location associated with this location (if - * applicable), or null. - * - * @return a secondary location or null - */ - @Nullable - public Location getSecondary() { - return mSecondary; - } - - /** - * Sets a secondary location for this location. - * - * @param secondary a secondary location associated with this location - */ - public void setSecondary(@Nullable Location secondary) { - mSecondary = secondary; - } - - /** - * Sets a custom message for this location. This is typically used for - * secondary locations, to describe the significance of this alternate - * location. For example, for a duplicate id warning, the primary location - * might say "This is a duplicate id", pointing to the second occurrence of - * id declaration, and then the secondary location could point to the - * original declaration with the custom message "Originally defined here". - * - * @param message the message to apply to this location - */ - public void setMessage(@NonNull String message) { - mMessage = message; - } - - /** - * Returns the custom message for this location, if any. This is typically - * used for secondary locations, to describe the significance of this - * alternate location. For example, for a duplicate id warning, the primary - * location might say "This is a duplicate id", pointing to the second - * occurrence of id declaration, and then the secondary location could point - * to the original declaration with the custom message - * "Originally defined here". - * - * @return the custom message for this location, or null - */ - @Nullable - public String getMessage() { - return mMessage; - } - - /** - * Sets the client data associated with this location. This is an optional - * field which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @param clientData the data to store with this location - */ - public void setClientData(@Nullable Object clientData) { - mClientData = clientData; - } - - /** - * Returns the client data associated with this location - an optional field - * which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @return the data associated with this location - */ - @Nullable - public Object getClientData() { - return mClientData; - } - - @Override - public String toString() { - return "Location [file=" + mFile + ", start=" + mStart + ", end=" + mEnd + ", message=" - + mMessage + ']'; - } - - /** - * Creates a new location for the given file - * - * @param file the file to create a location for - * @return a new location - */ - @NonNull - public static Location create(@NonNull File file) { - return new Location(file, null /*start*/, null /*end*/); - } - - /** - * Creates a new location for the given file and starting and ending - * positions. - * - * @param file the file containing the positions - * @param start the starting position - * @param end the ending position - * @return a new location - */ - @NonNull - public static Location create( - @NonNull File file, - @NonNull Position start, - @Nullable Position end) { - return new Location(file, start, end); - } - - /** - * Creates a new location for the given file, with the given contents, for - * the given offset range. - * - * @param file the file containing the location - * @param contents the current contents of the file - * @param startOffset the starting offset - * @param endOffset the ending offset - * @return a new location - */ - @NonNull - public static Location create( - @NonNull File file, - @Nullable String contents, - int startOffset, - int endOffset) { - if (startOffset < 0 || endOffset < startOffset) { - throw new IllegalArgumentException("Invalid offsets"); - } - - if (contents == null) { - return new Location(file, - new DefaultPosition(-1, -1, startOffset), - new DefaultPosition(-1, -1, endOffset)); - } - - int size = contents.length(); - endOffset = Math.min(endOffset, size); - startOffset = Math.min(startOffset, endOffset); - Position start = null; - int line = 0; - int lineOffset = 0; - char prev = 0; - for (int offset = 0; offset <= size; offset++) { - if (offset == startOffset) { - start = new DefaultPosition(line, offset - lineOffset, offset); - } - if (offset == endOffset) { - Position end = new DefaultPosition(line, offset - lineOffset, offset); - return new Location(file, start, end); - } - char c = contents.charAt(offset); - if (c == '\n') { - lineOffset = offset + 1; - if (prev != '\r') { - line++; - } - } else if (c == '\r') { - line++; - lineOffset = offset + 1; - } - prev = c; - } - return create(file); - } - - /** - * Creates a new location for the given file, with the given contents, for - * the given line number. - * - * @param file the file containing the location - * @param contents the current contents of the file - * @param line the line number (0-based) for the position - * @return a new location - */ - @NonNull - public static Location create(@NonNull File file, @NonNull String contents, int line) { - return create(file, contents, line, null, null, null); - } - - /** - * Creates a new location for the given file, with the given contents, for - * the given line number. - * - * @param file the file containing the location - * @param contents the current contents of the file - * @param line the line number (0-based) for the position - * @param patternStart an optional pattern to search for from the line - * match; if found, adjust the column and offsets to begin at the - * pattern start - * @param patternEnd an optional pattern to search for behind the start - * pattern; if found, adjust the end offset to match the end of - * the pattern - * @param hints optional additional information regarding the pattern search - * @return a new location - */ - @NonNull - public static Location create(@NonNull File file, @NonNull String contents, int line, - @Nullable String patternStart, @Nullable String patternEnd, - @Nullable SearchHints hints) { - int currentLine = 0; - int offset = 0; - while (currentLine < line) { - offset = contents.indexOf('\n', offset); - if (offset == -1) { - return create(file); - } - currentLine++; - offset++; - } - - if (line == currentLine) { - if (patternStart != null) { - SearchDirection direction = SearchDirection.NEAREST; - if (hints != null) { - direction = hints.mDirection; - } - - int index; - if (direction == SearchDirection.BACKWARD) { - index = findPreviousMatch(contents, offset, patternStart, hints); - line = adjustLine(contents, line, offset, index); - } else if (direction == SearchDirection.EOL_BACKWARD) { - int lineEnd = contents.indexOf('\n', offset); - if (lineEnd == -1) { - lineEnd = contents.length(); - } - - index = findPreviousMatch(contents, lineEnd, patternStart, hints); - line = adjustLine(contents, line, offset, index); - } else if (direction == SearchDirection.FORWARD) { - index = findNextMatch(contents, offset, patternStart, hints); - line = adjustLine(contents, line, offset, index); - } else { - assert direction == SearchDirection.NEAREST; - - int before = findPreviousMatch(contents, offset, patternStart, hints); - int after = findNextMatch(contents, offset, patternStart, hints); - - if (before == -1) { - index = after; - line = adjustLine(contents, line, offset, index); - } else if (after == -1) { - index = before; - line = adjustLine(contents, line, offset, index); - } else if (offset - before < after - offset) { - index = before; - line = adjustLine(contents, line, offset, index); - } else { - index = after; - line = adjustLine(contents, line, offset, index); - } - } - - if (index != -1) { - int lineStart = contents.lastIndexOf('\n', index); - if (lineStart == -1) { - lineStart = 0; - } else { - lineStart++; // was pointing to the previous line's CR, not line start - } - int column = index - lineStart; - if (patternEnd != null) { - int end = contents.indexOf(patternEnd, offset + patternStart.length()); - if (end != -1) { - return new Location(file, new DefaultPosition(line, column, index), - new DefaultPosition(line, -1, end + patternEnd.length())); - } - } else if (hints != null && (hints.isJavaSymbol() || hints.isWholeWord())) { - if (hints.isConstructor() && contents.startsWith(SUPER_KEYWORD, index)) { - patternStart = SUPER_KEYWORD; - } - return new Location(file, new DefaultPosition(line, column, index), - new DefaultPosition(line, column + patternStart.length(), - index + patternStart.length())); - } - return new Location(file, new DefaultPosition(line, column, index), - new DefaultPosition(line, column, index + patternStart.length())); - } - } - - Position position = new DefaultPosition(line, -1, offset); - return new Location(file, position, position); - } - - return create(file); - } - - private static int findPreviousMatch(@NonNull String contents, int offset, String pattern, - @Nullable SearchHints hints) { - while (true) { - int index = contents.lastIndexOf(pattern, offset); - if (index == -1) { - return -1; - } else { - if (isMatch(contents, index, pattern, hints)) { - return index; - } else { - offset = index - pattern.length(); - } - } - } - } - - private static int findNextMatch(@NonNull String contents, int offset, String pattern, - @Nullable SearchHints hints) { - int constructorIndex = -1; - if (hints != null && hints.isConstructor()) { - // Special condition: See if the call is referenced as "super" instead. - assert hints.isWholeWord(); - int index = contents.indexOf(SUPER_KEYWORD, offset); - if (index != -1 && isMatch(contents, index, SUPER_KEYWORD, hints)) { - constructorIndex = index; - } - } - - while (true) { - int index = contents.indexOf(pattern, offset); - if (index == -1) { - return constructorIndex; - } else { - if (isMatch(contents, index, pattern, hints)) { - if (constructorIndex != -1) { - return Math.min(constructorIndex, index); - } - return index; - } else { - offset = index + pattern.length(); - } - } - } - } - - private static boolean isMatch(@NonNull String contents, int offset, String pattern, - @Nullable SearchHints hints) { - if (!contents.startsWith(pattern, offset)) { - return false; - } - - if (hints != null) { - char prevChar = offset > 0 ? contents.charAt(offset - 1) : 0; - int lastIndex = offset + pattern.length() - 1; - char nextChar = lastIndex < contents.length() - 1 ? contents.charAt(lastIndex + 1) : 0; - - if (hints.isWholeWord() && (Character.isLetter(prevChar) - || Character.isLetter(nextChar))) { - return false; - - } - - if (hints.isJavaSymbol()) { - if (Character.isJavaIdentifierPart(prevChar) - || Character.isJavaIdentifierPart(nextChar)) { - return false; - } - - if (prevChar == '"') { - return false; - } - - // TODO: Additional validation to see if we're in a comment, string, etc. - // This will require lexing from the beginning of the buffer. - } - - if (hints.isConstructor() && SUPER_KEYWORD.equals(pattern)) { - // Only looking for super(), not super.x, so assert that the next - // non-space character is ( - int index = lastIndex + 1; - while (index < contents.length() - 1) { - char c = contents.charAt(index); - if (c == '(') { - break; - } else if (!Character.isWhitespace(c)) { - return false; - } - index++; - } - } - } - - return true; - } - - private static int adjustLine(String doc, int line, int offset, int newOffset) { - if (newOffset == -1) { - return line; - } - - if (newOffset < offset) { - return line - countLines(doc, newOffset, offset); - } else { - return line + countLines(doc, offset, newOffset); - } - } - - private static int countLines(String doc, int start, int end) { - int lines = 0; - for (int offset = start; offset < end; offset++) { - char c = doc.charAt(offset); - if (c == '\n') { - lines++; - } - } - - return lines; - } - - /** - * Reverses the secondary location list initiated by the given location - * - * @param location the first location in the list - * @return the first location in the reversed list - */ - public static Location reverse(Location location) { - Location next = location.getSecondary(); - location.setSecondary(null); - while (next != null) { - Location nextNext = next.getSecondary(); - next.setSecondary(location); - location = next; - next = nextNext; - } - - return location; - } - - /** - * A {@link Handle} is a reference to a location. The point of a location - * handle is to be able to create them cheaply, and then resolve them into - * actual locations later (if needed). This makes it possible to for example - * delay looking up line numbers, for locations that are offset based. - */ - public interface Handle { - /** - * Compute a full location for the given handle - * - * @return create a location for this handle - */ - @NonNull - Location resolve(); - - /** - * Sets the client data associated with this location. This is an optional - * field which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @param clientData the data to store with this location - */ - void setClientData(@Nullable Object clientData); - - /** - * Returns the client data associated with this location - an optional field - * which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @return the data associated with this location - */ - @Nullable - Object getClientData(); - } - - /** A default {@link Handle} implementation for simple file offsets */ - public static class DefaultLocationHandle implements Handle { - private final File mFile; - private final String mContents; - private final int mStartOffset; - private final int mEndOffset; - private Object mClientData; - - /** - * Constructs a new {@link DefaultLocationHandle} - * - * @param context the context pointing to the file and its contents - * @param startOffset the start offset within the file - * @param endOffset the end offset within the file - */ - public DefaultLocationHandle(@NonNull Context context, int startOffset, int endOffset) { - mFile = context.file; - mContents = context.getContents(); - mStartOffset = startOffset; - mEndOffset = endOffset; - } - - @Override - @NonNull - public Location resolve() { - return create(mFile, mContents, mStartOffset, mEndOffset); - } - - @Override - public void setClientData(@Nullable Object clientData) { - mClientData = clientData; - } - - @Override - @Nullable - public Object getClientData() { - return mClientData; - } - } - - /** - * Whether to look forwards, or backwards, or in both directions, when - * searching for a pattern in the source code to determine the right - * position range for a given symbol. - * <p> - * When dealing with bytecode for example, there are only line number entries - * within method bodies, so when searching for the method declaration, we should only - * search backwards from the first line entry in the method. - */ - public enum SearchDirection { - /** Only search forwards */ - FORWARD, - - /** Only search backwards */ - BACKWARD, - - /** Search backwards from the current end of line (normally it's the beginning of - * the current line) */ - EOL_BACKWARD, - - /** - * Search both forwards and backwards from the given line, and prefer - * the match that is closest - */ - NEAREST, - } - - /** - * Extra information pertaining to finding a symbol in a source buffer, - * used by {@link Location#create(File, String, int, String, String, SearchHints)} - */ - public static class SearchHints { - /** - * the direction to search for the nearest match in (provided - * {@code patternStart} is non null) - */ - @NonNull - private final SearchDirection mDirection; - - /** Whether the matched pattern should be a whole word */ - private boolean mWholeWord; - - /** - * Whether the matched pattern should be a Java symbol (so for example, - * a match inside a comment or string literal should not be used) - */ - private boolean mJavaSymbol; - - /** - * Whether the matched pattern corresponds to a constructor; if so, look for - * some other possible source aliases too, such as "super". - */ - private boolean mConstructor; - - private SearchHints(@NonNull SearchDirection direction) { - super(); - mDirection = direction; - } - - /** - * Constructs a new {@link SearchHints} object - * - * @param direction the direction to search in for the pattern - * @return a new @link SearchHints} object - */ - @NonNull - public static SearchHints create(@NonNull SearchDirection direction) { - return new SearchHints(direction); - } - - /** - * Indicates that pattern matches should apply to whole words only - - * @return this, for constructor chaining - */ - @NonNull - public SearchHints matchWholeWord() { - mWholeWord = true; - - return this; - } - - /** @return true if the pattern match should be for whole words only */ - public boolean isWholeWord() { - return mWholeWord; - } - - /** - * Indicates that pattern matches should apply to Java symbols only - * - * @return this, for constructor chaining - */ - @NonNull - public SearchHints matchJavaSymbol() { - mJavaSymbol = true; - mWholeWord = true; - - return this; - } - - /** @return true if the pattern match should be for Java symbols only */ - public boolean isJavaSymbol() { - return mJavaSymbol; - } - - /** - * Indicates that pattern matches should apply to constructors. If so, look for - * some other possible source aliases too, such as "super". - * - * @return this, for constructor chaining - */ - @NonNull - public SearchHints matchConstructor() { - mConstructor = true; - mWholeWord = true; - mJavaSymbol = true; - - return this; - } - - /** @return true if the pattern match should be for a constructor */ - public boolean isConstructor() { - return mConstructor; - } - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Position.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Position.java deleted file mode 100644 index 6407cc9..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Position.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.detector.api; - -import com.google.common.annotations.Beta; - -/** - * Information about a position in a file/document. - * <p/> - * <b>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.</b> - */ -@Beta -public abstract class Position { - /** - * Returns the line number (0-based where the first line is line 0) - * - * @return the 0-based line number - */ - public abstract int getLine(); - - /** - * The character offset - * - * @return the 0-based character offset - */ - public abstract int getOffset(); - - /** - * Returns the column number (where the first character on the line is 0), - * or -1 if unknown - * - * @return the 0-based column number - */ - public abstract int getColumn(); -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Project.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Project.java deleted file mode 100644 index 968b9b1..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Project.java +++ /dev/null @@ -1,929 +0,0 @@ -/* - * 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.detector.api; - -import static com.android.SdkConstants.ANDROID_LIBRARY; -import static com.android.SdkConstants.ANDROID_LIBRARY_REFERENCE_FORMAT; -import static com.android.SdkConstants.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_MIN_SDK_VERSION; -import static com.android.SdkConstants.ATTR_PACKAGE; -import static com.android.SdkConstants.ATTR_TARGET_SDK_VERSION; -import static com.android.SdkConstants.PROGUARD_CONFIG; -import static com.android.SdkConstants.PROJECT_PROPERTIES; -import static com.android.SdkConstants.RES_FOLDER; -import static com.android.SdkConstants.TAG_USES_SDK; -import static com.android.SdkConstants.VALUE_TRUE; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.CircularDependencyException; -import com.android.tools.lint.client.api.Configuration; -import com.android.tools.lint.client.api.LintClient; -import com.android.tools.lint.client.api.SdkInfo; -import com.google.common.annotations.Beta; -import com.google.common.base.Charsets; -import com.google.common.io.Closeables; -import com.google.common.io.Files; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A project contains information about an Android project being scanned for - * Lint errors. - * <p> - * <b>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.</b> - */ -@Beta -public class Project { - private final LintClient mClient; - private final File mDir; - private final File mReferenceDir; - private Configuration mConfiguration; - private String mPackage; - private int mMinSdk = 1; - private int mTargetSdk = -1; - private int mBuildSdk = -1; - private boolean mLibrary; - private String mName; - private String mProguardPath; - private boolean mMergeManifests; - - /** The SDK info, if any */ - private SdkInfo mSdkInfo; - - /** - * If non null, specifies a non-empty list of specific files under this - * project which should be checked. - */ - private List<File> mFiles; - private List<File> mJavaSourceFolders; - private List<File> mJavaClassFolders; - private List<File> mJavaLibraries; - private List<Project> mDirectLibraries; - private List<Project> mAllLibraries; - private boolean mReportIssues = true; - - /** - * Creates a new {@link Project} for the given directory. - * - * @param client the tool running the lint check - * @param dir the root directory of the project - * @param referenceDir See {@link #getReferenceDir()}. - * @return a new {@link Project} - */ - @NonNull - public static Project create( - @NonNull LintClient client, - @NonNull File dir, - @NonNull File referenceDir) { - return new Project(client, dir, referenceDir); - } - - /** Creates a new Project. Use one of the factory methods to create. */ - private Project( - @NonNull LintClient client, - @NonNull File dir, - @NonNull File referenceDir) { - mClient = client; - mDir = dir; - mReferenceDir = referenceDir; - - try { - // Read properties file and initialize library state - Properties properties = new Properties(); - File propFile = new File(dir, PROJECT_PROPERTIES); - if (propFile.exists()) { - @SuppressWarnings("resource") // Eclipse doesn't know about Closeables.closeQuietly - BufferedInputStream is = new BufferedInputStream(new FileInputStream(propFile)); - try { - properties.load(is); - String value = properties.getProperty(ANDROID_LIBRARY); - mLibrary = VALUE_TRUE.equals(value); - mProguardPath = properties.getProperty(PROGUARD_CONFIG); - mMergeManifests = VALUE_TRUE.equals(properties.getProperty( - "manifestmerger.enabled")); //$NON-NLS-1$ - String target = properties.getProperty("target"); //$NON-NLS-1$ - if (target != null) { - int index = target.lastIndexOf('-'); - if (index == -1) { - index = target.lastIndexOf(':'); - } - if (index != -1) { - String versionString = target.substring(index + 1); - try { - mBuildSdk = Integer.parseInt(versionString); - } catch (NumberFormatException nufe) { - mClient.log(Severity.WARNING, null, - "Unexpected build target format: %1$s", target); - } - } - } - - for (int i = 1; i < 1000; i++) { - String key = String.format(ANDROID_LIBRARY_REFERENCE_FORMAT, i); - String library = properties.getProperty(key); - if (library == null || library.isEmpty()) { - // No holes in the numbering sequence is allowed - break; - } - - File libraryDir = new File(dir, library).getCanonicalFile(); - - if (mDirectLibraries == null) { - mDirectLibraries = new ArrayList<Project>(); - } - - // Adjust the reference dir to be a proper prefix path of the - // library dir - File libraryReferenceDir = referenceDir; - if (!libraryDir.getPath().startsWith(referenceDir.getPath())) { - // Symlinks etc might have been resolved, so do those to - // the reference dir as well - libraryReferenceDir = libraryReferenceDir.getCanonicalFile(); - if (!libraryDir.getPath().startsWith(referenceDir.getPath())) { - File file = libraryReferenceDir; - while (file != null && !file.getPath().isEmpty()) { - if (libraryDir.getPath().startsWith(file.getPath())) { - libraryReferenceDir = file; - break; - } - file = file.getParentFile(); - } - } - } - - try { - Project libraryPrj = client.getProject(libraryDir, libraryReferenceDir); - mDirectLibraries.add(libraryPrj); - // By default, we don't report issues in inferred library projects. - // The driver will set report = true for those library explicitly - // requested. - libraryPrj.setReportIssues(false); - } catch (CircularDependencyException e) { - e.setProject(this); - e.setLocation(Location.create(propFile)); - throw e; - } - } - } finally { - Closeables.closeQuietly(is); - } - } - } catch (IOException ioe) { - client.log(ioe, "Initializing project state"); - } - - if (mDirectLibraries != null) { - mDirectLibraries = Collections.unmodifiableList(mDirectLibraries); - } else { - mDirectLibraries = Collections.emptyList(); - } - } - - @Override - public String toString() { - return "Project [dir=" + mDir + ']'; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mDir == null) ? 0 : mDir.hashCode()); - return result; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Project other = (Project) obj; - if (mDir == null) { - if (other.mDir != null) - return false; - } else if (!mDir.equals(other.mDir)) - return false; - return true; - } - - /** - * Adds the given file to the list of files which should be checked in this - * project. If no files are added, the whole project will be checked. - * - * @param file the file to be checked - */ - public void addFile(@NonNull File file) { - if (mFiles == null) { - mFiles = new ArrayList<File>(); - } - mFiles.add(file); - } - - /** - * The list of files to be checked in this project. If null, the whole - * project should be checked. - * - * @return the subset of files to be checked, or null for the whole project - */ - @Nullable - public List<File> getSubset() { - return mFiles; - } - - /** - * Returns the list of source folders for Java source files - * - * @return a list of source folders to search for .java files - */ - @NonNull - public List<File> getJavaSourceFolders() { - if (mJavaSourceFolders == null) { - if (isAospFrameworksProject(mDir)) { - return Collections.singletonList(new File(mDir, "java")); //$NON-NLS-1$ - } - if (isAospBuildEnvironment()) { - String top = getAospTop(); - if (mDir.getAbsolutePath().startsWith(top)) { - mJavaSourceFolders = getAospJavaSourcePath(); - return mJavaSourceFolders; - } - } - - mJavaSourceFolders = mClient.getJavaSourceFolders(this); - } - - return mJavaSourceFolders; - } - - /** - * Returns the list of output folders for class files - * @return a list of output folders to search for .class files - */ - @NonNull - public List<File> getJavaClassFolders() { - if (mJavaClassFolders == null) { - if (isAospFrameworksProject(mDir)) { - File top = mDir.getParentFile().getParentFile().getParentFile(); - if (top != null) { - File out = new File(top, "out"); - if (out.exists()) { - String relative = - "target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar"; - File jar = new File(out, relative.replace('/', File.separatorChar)); - if (jar.exists()) { - mJavaClassFolders = Collections.singletonList(jar); - return mJavaClassFolders; - } - } - } - } - if (isAospBuildEnvironment()) { - String top = getAospTop(); - if (mDir.getAbsolutePath().startsWith(top)) { - mJavaClassFolders = getAospJavaClassPath(); - return mJavaClassFolders; - } - } - - mJavaClassFolders = mClient.getJavaClassFolders(this); - } - return mJavaClassFolders; - } - - /** - * Returns the list of Java libraries (typically .jar files) that this - * project depends on. Note that this refers to jar libraries, not Android - * library projects which are processed in a separate pass with their own - * source and class folders. - * - * @return a list of .jar files (or class folders) that this project depends - * on. - */ - @NonNull - public List<File> getJavaLibraries() { - if (mJavaLibraries == null) { - // AOSP builds already merge libraries and class folders into - // the single classes.jar file, so these have already been processed - // in getJavaClassFolders. - - mJavaLibraries = mClient.getJavaLibraries(this); - } - - return mJavaLibraries; - } - - /** - * Returns the resource folder. - * - * @return a file pointing to the resource folder, or null if the project - * does not contain any resources - */ - @NonNull - public List<File> getResourceFolders() { - List<File> folders = mClient.getResourceFolders(this); - - if (folders.size() == 1 && isAospFrameworksProject(mDir)) { - // No manifest file for this project: just init the manifest values here - mMinSdk = mTargetSdk = SdkConstants.HIGHEST_KNOWN_API; - File folder = new File(folders.get(0), RES_FOLDER); - if (!folder.exists()) { - folders = Collections.emptyList(); - } - } - - return folders; - } - - /** - * Returns the relative path of a given file relative to the user specified - * directory (which is often the project directory but sometimes a higher up - * directory when a directory tree is being scanned - * - * @param file the file under this project to check - * @return the path relative to the reference directory (often the project directory) - */ - @NonNull - public String getDisplayPath(@NonNull File file) { - String path = file.getPath(); - String referencePath = mReferenceDir.getPath(); - if (path.startsWith(referencePath)) { - int length = referencePath.length(); - if (path.length() > length && path.charAt(length) == File.separatorChar) { - length++; - } - - return path.substring(length); - } - - return path; - } - - /** - * Returns the relative path of a given file within the current project. - * - * @param file the file under this project to check - * @return the path relative to the project - */ - @NonNull - public String getRelativePath(@NonNull File file) { - String path = file.getPath(); - String referencePath = mDir.getPath(); - if (path.startsWith(referencePath)) { - int length = referencePath.length(); - if (path.length() > length && path.charAt(length) == File.separatorChar) { - length++; - } - - return path.substring(length); - } - - return path; - } - - /** - * Returns the project root directory - * - * @return the dir - */ - @NonNull - public File getDir() { - return mDir; - } - - /** - * Returns the original user supplied directory where the lint search - * started. For example, if you run lint against {@code /tmp/foo}, and it - * finds a project to lint in {@code /tmp/foo/dev/src/project1}, then the - * {@code dir} is {@code /tmp/foo/dev/src/project1} and the - * {@code referenceDir} is {@code /tmp/foo/}. - * - * @return the reference directory, never null - */ - @NonNull - public File getReferenceDir() { - return mReferenceDir; - } - - /** - * Gets the configuration associated with this project - * - * @return the configuration associated with this project - */ - @NonNull - public Configuration getConfiguration() { - if (mConfiguration == null) { - mConfiguration = mClient.getConfiguration(this); - } - return mConfiguration; - } - - /** - * Returns the application package specified by the manifest - * - * @return the application package, or null if unknown - */ - @Nullable - public String getPackage() { - //assert !mLibrary; // Should call getPackage on the master project, not the library - // Assertion disabled because you might be running lint on a standalone library project. - - return mPackage; - } - - /** - * Returns the minimum API level requested by the manifest, or -1 if not - * specified - * - * @return the minimum API level or -1 if unknown - */ - public int getMinSdk() { - //assert !mLibrary; // Should call getMinSdk on the master project, not the library - // Assertion disabled because you might be running lint on a standalone library project. - - return mMinSdk; - } - - /** - * Returns the target API level specified by the manifest, or -1 if not - * specified - * - * @return the target API level or -1 if unknown - */ - public int getTargetSdk() { - //assert !mLibrary; // Should call getTargetSdk on the master project, not the library - // Assertion disabled because you might be running lint on a standalone library project. - - return mTargetSdk; - } - - /** - * Returns the target API used to build the project, or -1 if not known - * - * @return the build target API or -1 if unknown - */ - public int getBuildSdk() { - return mBuildSdk; - } - - /** - * Initialized the manifest state from the given manifest model - * - * @param document the DOM document for the manifest XML document - */ - public void readManifest(@NonNull Document document) { - Element root = document.getDocumentElement(); - if (root == null) { - return; - } - - mPackage = root.getAttribute(ATTR_PACKAGE); - - // Initialize minSdk and targetSdk - NodeList usesSdks = root.getElementsByTagName(TAG_USES_SDK); - if (usesSdks.getLength() > 0) { - Element element = (Element) usesSdks.item(0); - - String minSdk = null; - if (element.hasAttributeNS(ANDROID_URI, ATTR_MIN_SDK_VERSION)) { - minSdk = element.getAttributeNS(ANDROID_URI, ATTR_MIN_SDK_VERSION); - } - if (minSdk != null) { - try { - mMinSdk = Integer.valueOf(minSdk); - } catch (NumberFormatException e) { - mMinSdk = 1; - } - } - - String targetSdk = null; - if (element.hasAttributeNS(ANDROID_URI, ATTR_TARGET_SDK_VERSION)) { - targetSdk = element.getAttributeNS(ANDROID_URI, ATTR_TARGET_SDK_VERSION); - } else if (minSdk != null) { - targetSdk = minSdk; - } - if (targetSdk != null) { - try { - mTargetSdk = Integer.valueOf(targetSdk); - } catch (NumberFormatException e) { - // TODO: Handle codenames? - mTargetSdk = -1; - } - } - } else if (isAospBuildEnvironment()) { - extractAospMinSdkVersion(); - } - } - - /** - * Returns true if this project is an Android library project - * - * @return true if this project is an Android library project - */ - public boolean isLibrary() { - return mLibrary; - } - - /** - * Returns the list of library projects referenced by this project - * - * @return the list of library projects referenced by this project, never - * null - */ - @NonNull - public List<Project> getDirectLibraries() { - return mDirectLibraries; - } - - /** - * Returns the transitive closure of the library projects for this project - * - * @return the transitive closure of the library projects for this project - */ - @NonNull - public List<Project> getAllLibraries() { - if (mAllLibraries == null) { - if (mDirectLibraries.isEmpty()) { - return mDirectLibraries; - } - - List<Project> all = new ArrayList<Project>(); - addLibraryProjects(all); - mAllLibraries = all; - } - - return mAllLibraries; - } - - /** - * Adds this project's library project and their library projects - * recursively into the given collection of projects - * - * @param collection the collection to add the projects into - */ - private void addLibraryProjects(@NonNull Collection<Project> collection) { - for (Project library : mDirectLibraries) { - collection.add(library); - // Recurse - library.addLibraryProjects(collection); - } - } - - /** - * Gets the SDK info for the current project. - * - * @return the SDK info for the current project, never null - */ - @NonNull - public SdkInfo getSdkInfo() { - if (mSdkInfo == null) { - mSdkInfo = mClient.getSdkInfo(this); - } - - return mSdkInfo; - } - - /** - * Gets the path to the manifest file in this project, if it exists - * - * @return the path to the manifest file, or null if it does not exist - */ - @Nullable - public File getManifestFile() { - File manifestFile = new File(mDir, ANDROID_MANIFEST_XML); - if (manifestFile.exists()) { - return manifestFile; - } - - return null; - } - - /** - * Returns the proguard path configured for this project, or null if ProGuard is - * not configured. - * - * @return the proguard path, or null - */ - @Nullable - public String getProguardPath() { - return mProguardPath; - } - - /** - * Returns the name of the project - * - * @return the name of the project, never null - */ - @NonNull - public String getName() { - if (mName == null) { - mName = mClient.getProjectName(this); - } - - return mName; - } - - /** - * Sets the name of the project - * - * @param name the name of the project, never null - */ - public void setName(@NonNull String name) { - assert !name.isEmpty(); - mName = name; - } - - /** - * Sets whether lint should report issues in this project. See - * {@link #getReportIssues()} for a full description of what that means. - * - * @param reportIssues whether lint should report issues in this project - */ - public void setReportIssues(boolean reportIssues) { - mReportIssues = reportIssues; - } - - /** - * Returns whether lint should report issues in this project. - * <p> - * If a user specifies a project and its library projects for analysis, then - * those library projects are all "included", and all errors found in all - * the projects are reported. But if the user is only running lint on the - * main project, we shouldn't report errors in any of the library projects. - * We still need to <b>consider</b> them for certain types of checks, such - * as determining whether resources found in the main project are unused, so - * the detectors must still get a chance to look at these projects. The - * {@code #getReportIssues()} attribute is used for this purpose. - * - * @return whether lint should report issues in this project - */ - public boolean getReportIssues() { - return mReportIssues; - } - - /** - * Sets whether manifest merging is in effect. - * - * @param merging whether manifest merging is in effect - */ - public void setMergingManifests(boolean merging) { - mMergeManifests = merging; - } - - /** - * Returns whether manifest merging is in effect - * - * @return true if manifests in library projects should be merged into main projects - */ - public boolean isMergingManifests() { - return mMergeManifests; - } - - - // --------------------------------------------------------------------------- - // Support for running lint on the AOSP source tree itself - - private static Boolean sAospBuild; - - /** Is lint running in an AOSP build environment */ - private static boolean isAospBuildEnvironment() { - if (sAospBuild == null) { - sAospBuild = getAospTop() != null; - } - - return sAospBuild.booleanValue(); - } - - /** - * Is this the frameworks AOSP project? Needs some hardcoded support since - * it doesn't have a manifest file, etc. - * - * @param dir the project directory to check - * @return true if this looks like the frameworks/base/core project - */ - static boolean isAospFrameworksProject(@NonNull File dir) { - if (!dir.getPath().endsWith("core")) { //$NON-NLS-1$ - return false; - } - - File parent = dir.getParentFile(); - if (parent == null || !parent.getName().equals("base")) { //$NON-NLS-1$ - return false; - } - - parent = parent.getParentFile(); - if (parent == null || !parent.getName().equals("frameworks")) { //$NON-NLS-1$ - return false; - } - - return true; - } - - /** Get the root AOSP dir, if any */ - private static String getAospTop() { - return System.getenv("ANDROID_BUILD_TOP"); //$NON-NLS-1$ - } - - /** Get the host out directory in AOSP, if any */ - private static String getAospHostOut() { - return System.getenv("ANDROID_HOST_OUT"); //$NON-NLS-1$ - } - - /** Get the product out directory in AOSP, if any */ - private static String getAospProductOut() { - return System.getenv("ANDROID_PRODUCT_OUT"); //$NON-NLS-1$ - } - - private List<File> getAospJavaSourcePath() { - List<File> sources = new ArrayList<File>(2); - // Normal sources - File src = new File(mDir, "src"); //$NON-NLS-1$ - if (src.exists()) { - sources.add(src); - } - - // Generates sources - for (File dir : getIntermediateDirs()) { - File classes = new File(dir, "src"); //$NON-NLS-1$ - if (classes.exists()) { - sources.add(classes); - } - } - - if (sources.isEmpty()) { - mClient.log(null, - "Warning: Could not find sources or generated sources for project %1$s", - getName()); - } - - return sources; - } - - private List<File> getAospJavaClassPath() { - List<File> classDirs = new ArrayList<File>(1); - - for (File dir : getIntermediateDirs()) { - File classes = new File(dir, "classes"); //$NON-NLS-1$ - if (classes.exists()) { - classDirs.add(classes); - } else { - classes = new File(dir, "classes.jar"); //$NON-NLS-1$ - if (classes.exists()) { - classDirs.add(classes); - } - } - } - - if (classDirs.isEmpty()) { - mClient.log(null, - "No bytecode found: Has the project been built? (%1$s)", getName()); - } - - return classDirs; - } - - /** Find the _intermediates directories for a given module name */ - private List<File> getIntermediateDirs() { - // See build/core/definitions.mk and in particular the "intermediates-dir-for" definition - List<File> intermediates = new ArrayList<File>(); - - // TODO: Look up the module name, e.g. LOCAL_MODULE. However, - // some Android.mk files do some complicated things with it - and most - // projects use the same module name as the directory name. - String moduleName = mDir.getName(); - - String top = getAospTop(); - final String[] outFolders = new String[] { - top + "/out/host/common/obj", //$NON-NLS-1$ - top + "/out/target/common/obj", //$NON-NLS-1$ - getAospHostOut() + "/obj", //$NON-NLS-1$ - getAospProductOut() + "/obj" //$NON-NLS-1$ - }; - final String[] moduleClasses = new String[] { - "APPS", //$NON-NLS-1$ - "JAVA_LIBRARIES", //$NON-NLS-1$ - }; - - for (String out : outFolders) { - assert new File(out.replace('/', File.separatorChar)).exists() : out; - for (String moduleClass : moduleClasses) { - String path = out + '/' + moduleClass + '/' + moduleName - + "_intermediates"; //$NON-NLS-1$ - File file = new File(path.replace('/', File.separatorChar)); - if (file.exists()) { - intermediates.add(file); - } - } - } - - return intermediates; - } - - private void extractAospMinSdkVersion() { - // Is the SDK level specified by a Makefile? - boolean found = false; - File makefile = new File(mDir, "Android.mk"); //$NON-NLS-1$ - if (makefile.exists()) { - try { - List<String> lines = Files.readLines(makefile, Charsets.UTF_8); - Pattern p = Pattern.compile("LOCAL_SDK_VERSION\\s*:=\\s*(.*)"); //$NON-NLS-1$ - for (String line : lines) { - line = line.trim(); - Matcher matcher = p.matcher(line); - if (matcher.matches()) { - found = true; - String version = matcher.group(1); - if (version.equals("current")) { //$NON-NLS-1$ - mMinSdk = findCurrentAospVersion(); - } else { - try { - mMinSdk = Integer.valueOf(version); - } catch (NumberFormatException e) { - // Codename - just use current - mMinSdk = findCurrentAospVersion(); - } - } - break; - } - } - } catch (IOException ioe) { - mClient.log(ioe, null); - } - } - - if (!found) { - mMinSdk = findCurrentAospVersion(); - } - } - - /** Cache for {@link #findCurrentAospVersion()} */ - private static int sCurrentVersion; - - /** In an AOSP build environment, identify the currently built image version, if available */ - private static int findCurrentAospVersion() { - if (sCurrentVersion < 1) { - File apiDir = new File(getAospTop(), "frameworks/base/api" //$NON-NLS-1$ - .replace('/', File.separatorChar)); - File[] apiFiles = apiDir.listFiles(); - if (apiFiles == null) { - sCurrentVersion = 1; - return sCurrentVersion; - } - int max = 1; - for (File apiFile : apiFiles) { - String name = apiFile.getName(); - int index = name.indexOf('.'); - if (index > 0) { - String base = name.substring(0, index); - if (Character.isDigit(base.charAt(0))) { - try { - int version = Integer.parseInt(base); - if (version > max) { - max = version; - } - } catch (NumberFormatException nufe) { - // pass - } - } - } - } - sCurrentVersion = max; - } - - return sCurrentVersion; - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/ResourceXmlDetector.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/ResourceXmlDetector.java deleted file mode 100644 index 68685c6..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/ResourceXmlDetector.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.google.common.annotations.Beta; - -import java.io.File; - -/** - * Specialized detector intended for XML resources. Detectors that apply to XML - * resources should extend this detector instead since it provides special - * iteration hooks that are more efficient. - * <p/> - * <b>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.</b> - */ -@Beta -public abstract class ResourceXmlDetector extends Detector implements Detector.XmlScanner { - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return LintUtils.isXmlFile(file); - } - - /** - * Returns whether this detector applies to the given folder type. This - * allows the detectors to be pruned from iteration, so for example when we - * are analyzing a string value file we don't need to look up detectors - * related to layout. - * - * @param folderType the folder type to be visited - * @return true if this detector can apply to resources in folders of the - * given type - */ - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return true; - } - - @Override - public void run(@NonNull Context context) { - // The infrastructure should never call this method on an xml detector since - // it will run the various visitors instead - assert false; - } -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Scope.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Scope.java deleted file mode 100644 index 0e33f1b..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Scope.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.google.common.annotations.Beta; - -import java.util.EnumSet; - -/** - * The scope of a detector is the set of files a detector must consider when - * performing its analysis. This can be used to determine when issues are - * potentially obsolete, whether a detector should re-run on a file save, etc. - * <p> - * <b>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.</b> - */ -@Beta -public enum Scope { - /** - * The analysis only considers a single XML resource file at a time. - * <p> - * Issues which are only affected by a single resource file can be checked - * for incrementally when a file is edited. - */ - RESOURCE_FILE, - - /** - * The analysis considers <b>all</b> the resource file. This scope must not - * be used in conjunction with {@link #RESOURCE_FILE}; an issue scope is - * either considering just a single resource file or all the resources, not - * both. - */ - ALL_RESOURCE_FILES, - - /** - * The analysis only considers a single Java source file at a time. - * <p> - * Issues which are only affected by a single Java source file can be - * checked for incrementally when a Java source file is edited. - */ - JAVA_FILE, - - /** - * The analysis considers <b>all</b> the Java source files together. - * <p> - * This flag is mutually exclusive with {@link #JAVA_FILE}. - */ - ALL_JAVA_FILES, - - /** - * The analysis only considers a single Java class file at a time. - * <p> - * Issues which are only affected by a single Java class file can be checked - * for incrementally when a Java source file is edited and then recompiled. - */ - CLASS_FILE, - - /** - * The analysis considers <b>all</b> the Java class files together. - * <p> - * This flag is mutually exclusive with {@link #CLASS_FILE}. - */ - ALL_CLASS_FILES, - - /** The analysis considers the manifest file */ - MANIFEST, - - /** The analysis considers the Proguard configuration file */ - PROGUARD_FILE, - - /** - * The analysis considers classes in the libraries for this project. These - * will be analyzed before the classes themselves. - */ - JAVA_LIBRARIES, - - /** - * Scope for other files. Issues that specify a custom scope will be called unconditionally. - * This will call {@link Detector#run(Context)}} on the detectors unconditionally. - */ - OTHER; - - /** - * Returns true if the given scope set corresponds to scanning a single file - * rather than a whole project - * - * @param scopes the scope set to check - * @return true if the scope set references a single file - */ - public static boolean checkSingleFile(@NonNull EnumSet<Scope> scopes) { - int size = scopes.size(); - if (size == 2) { - // When single checking a Java source file, we check both its Java source - // and the associated class files - return scopes.contains(JAVA_FILE) && scopes.contains(CLASS_FILE); - } else { - return size == 1 && - (scopes.contains(JAVA_FILE) - || scopes.contains(CLASS_FILE) - || scopes.contains(RESOURCE_FILE) - || scopes.contains(PROGUARD_FILE) - || scopes.contains(MANIFEST)); - } - } - - /** - * Returns the intersection of two scope sets - * - * @param scope1 the first set to intersect - * @param scope2 the second set to intersect - * @return the intersection of the two sets - */ - @NonNull - public static EnumSet<Scope> intersect( - @NonNull EnumSet<Scope> scope1, - @NonNull EnumSet<Scope> scope2) { - EnumSet<Scope> scope = EnumSet.copyOf(scope1); - scope.retainAll(scope2); - - return scope; - } - - /** All scopes: running lint on a project will check these scopes */ - public static final EnumSet<Scope> ALL = EnumSet.allOf(Scope.class); - /** Scope-set used for detectors which are affected by a single resource file */ - public static final EnumSet<Scope> RESOURCE_FILE_SCOPE = EnumSet.of(RESOURCE_FILE); - /** Scope-set used for detectors which scan all resources */ - public static final EnumSet<Scope> ALL_RESOURCES_SCOPE = EnumSet.of(ALL_RESOURCE_FILES); - /** Scope-set used for detectors which are affected by a single Java source file */ - public static final EnumSet<Scope> JAVA_FILE_SCOPE = EnumSet.of(JAVA_FILE); - /** Scope-set used for detectors which are affected by a single Java class file */ - public static final EnumSet<Scope> CLASS_FILE_SCOPE = EnumSet.of(CLASS_FILE); - /** Scope-set used for detectors which are affected by the manifest only */ - public static final EnumSet<Scope> MANIFEST_SCOPE = EnumSet.of(MANIFEST); - /** Scope-set used for detectors which correspond to some other context */ - public static final EnumSet<Scope> OTHER_SCOPE = EnumSet.of(OTHER); -} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Severity.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Severity.java deleted file mode 100644 index f74e6b5..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Severity.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.google.common.annotations.Beta; - -/** - * Severity of an issue found by lint - * <p/> - * <b>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.</b> - */ -@Beta -public enum Severity { - /** - * Fatal: Use sparingly because a warning marked as fatal will be - * considered critical and will abort Export APK etc in ADT - */ - @NonNull - FATAL("Fatal"), - - /** - * Errors: The issue is known to be a real error that must be addressed. - */ - @NonNull - ERROR("Error"), - - /** - * Warning: Probably a problem. - */ - @NonNull - WARNING("Warning"), - - /** - * Information only: Might not be a problem, but the check has found - * something interesting to say about the code. - */ - @NonNull - INFORMATIONAL("Information"), - - /** - * Ignore: The user doesn't want to see this issue - */ - @NonNull - IGNORE("Ignore"); - - @NonNull - private final String mDisplay; - - Severity(@NonNull String display) { - mDisplay = display; - } - - /** - * Returns a description of this severity suitable for display to the user - * - * @return a description of the severity - */ - @NonNull - public String getDescription() { - return mDisplay; - } -}
\ No newline at end of file diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Speed.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Speed.java deleted file mode 100644 index c68dab0..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Speed.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.google.common.annotations.Beta; - -/** - * Enum which describes the different computation speeds of various detectors - * <p> - * <b>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.</b> - */ -@Beta -public enum Speed { - /** The detector can run very quickly */ - FAST("Fast"), - - /** The detector runs reasonably fast */ - NORMAL("Normal"), - - /** The detector might take a long time to run */ - SLOW("Slow"); - - private final String mDisplayName; - - Speed(@NonNull String displayName) { - mDisplayName = displayName; - } - - /** - * Returns the user-visible description of the speed of the given - * detector - * - * @return the description of the speed to display to the user - */ - @NonNull - public String getDisplayName() { - return mDisplayName; - } -}
\ No newline at end of file diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/XmlContext.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/XmlContext.java deleted file mode 100644 index 34d6816..0000000 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/XmlContext.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.client.api.IDomParser; -import com.android.tools.lint.client.api.LintDriver; -import com.google.common.annotations.Beta; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -import java.io.File; - -/** - * A {@link Context} used when checking XML files. - * <p/> - * <b>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.</b> - */ -@Beta -public class XmlContext extends Context { - /** The XML parser */ - public IDomParser parser; - /** The XML document */ - public Document document; - private final ResourceFolderType mFolderType; - - /** - * Construct a new {@link XmlContext} - * - * @param driver the driver running through the checks - * @param project the project containing the file being checked - * @param main the main project if this project is a library project, or - * null if this is not a library project. The main project is - * the root project of all library projects, not necessarily the - * directly including project. - * @param file the file being checked - * @param folderType the {@link ResourceFolderType} of this file, if any - */ - public XmlContext( - @NonNull LintDriver driver, - @NonNull Project project, - @Nullable Project main, - @NonNull File file, - @Nullable ResourceFolderType folderType) { - super(driver, project, main, file); - mFolderType = folderType; - } - - /** - * Returns the location for the given node, which may be an element or an attribute. - * - * @param node the node to look up the location for - * @return the location for the node - */ - @NonNull - public Location getLocation(@NonNull Node node) { - if (parser != null) { - return parser.getLocation(this, node); - } - - return Location.create(file); - } - - /** - * Creates a new location within an XML text node - * - * @param textNode the text node - * @param begin the start offset within the text node (inclusive) - * @param end the end offset within the text node (exclusive) - * @return a new location - */ - @NonNull - public Location getLocation(@NonNull Node textNode, int begin, int end) { - assert textNode.getNodeType() == Node.TEXT_NODE; - if (parser != null) { - return parser.getLocation(this, textNode, begin, end); - } - - return Location.create(file); - } - - - /** - * Reports an issue applicable to a given DOM node. The DOM node is used as the - * scope to check for suppress lint annotations. - * - * @param issue the issue to report - * @param scope the DOM node scope the error applies to. The lint infrastructure - * will check whether there are suppress directives on this node (or its enclosing - * nodes) and if so suppress the warning without involving the client. - * @param location the location of the issue, or null if not known - * @param message the message for this warning - * @param data any associated data, or null - */ - public void report( - @NonNull Issue issue, - @Nullable Node scope, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - if (scope != null && mDriver.isSuppressed(issue, scope)) { - return; - } - super.report(issue, location, message, data); - } - - @Override - public void report( - @NonNull Issue issue, - @Nullable Location location, - @NonNull String message, - @Nullable Object data) { - // Warn if clients use the non-scoped form? No, there are cases where an - // XML detector's error isn't applicable to one particular location (or it's - // not feasible to compute it cheaply) - //mDriver.getClient().log(null, "Warning: Issue " + issue - // + " was reported without a scope node: Can't be suppressed."); - - // For now just check the document root itself - if (document != null && mDriver.isSuppressed(issue, document)) { - return; - } - - super.report(issue, location, message, data); - } - - /** - * Returns the resource folder type of this XML file, if any. - * - * @return the resource folder type or null - */ - @Nullable - public ResourceFolderType getResourceFolderType() { - return mFolderType; - } -} diff --git a/lint/libs/lint_checks/Android.mk b/lint/libs/lint_checks/Android.mk index 30fc979..1382715 100644 --- a/lint/libs/lint_checks/Android.mk +++ b/lint/libs/lint_checks/Android.mk @@ -1,13 +1,27 @@ # Copyright 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. + LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) +# The lint_checks code has moved to tools/base/lint_checks. +# The rule below uses the prebuilt lint_checks.jar. +# +# If you want to run the tests, cd to tools/base/lint_checks +# and run ./gradlew :lint_checks:test # Only compile source java files in this lib. -LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java) -LOCAL_JAVA_RESOURCE_DIRS := src/main/java -# If the dependency list is changed, etc/manifest.txt LOCAL_JAVA_LIBRARIES := \ common \ sdklib \ @@ -22,5 +36,8 @@ LOCAL_JAVA_LIBRARIES := \ LOCAL_MODULE := lint_checks LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_JAVA_LIBRARY) +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + ../../../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) + +include $(BUILD_HOST_PREBUILT) diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/AccessibilityDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/AccessibilityDetector.java deleted file mode 100644 index 4d069cc..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/AccessibilityDetector.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_CONTENT_DESCRIPTION; -import static com.android.SdkConstants.ATTR_HINT; -import static com.android.SdkConstants.ATTR_IMPORTANT_FOR_ACCESSIBILITY; -import static com.android.SdkConstants.IMAGE_BUTTON; -import static com.android.SdkConstants.IMAGE_VIEW; -import static com.android.SdkConstants.VALUE_NO; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -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.Attr; -import org.w3c.dom.Element; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -/** - * Check which looks for accessibility problems like missing content descriptions - * <p> - * TODO: Resolve styles and don't warn where styles are defining the content description - * (though this seems unusual; content descriptions are not typically generic enough to - * put in styles) - */ -public class AccessibilityDetector extends LayoutDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "ContentDescription", //$NON-NLS-1$ - "Ensures that image widgets provide a contentDescription", - "Non-textual widgets like ImageViews and ImageButtons should use the " + - "`contentDescription` attribute to specify a textual description of " + - "the widget such that screen readers and other accessibility tools " + - "can adequately describe the user interface.\n" + - "\n" + - "Note that elements in application screens that are purely decorative " + - "and do not provide any content or enable a user action should not " + - "have accessibility content descriptions. In this case, just suppress the " + - "lint warning with a tools:ignore=\"ContentDescription\" attribute.\n" + - "\n" + - "Note that for text fields, you should not set both the `hint` and the " + - "`contentDescription` attributes since the hint will never be shown. Just " + - "set the `hint`. See " + - "http://developer.android.com/guide/topics/ui/accessibility/checklist.html#special-cases.", - - Category.A11Y, - 3, - Severity.WARNING, - AccessibilityDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new {@link AccessibilityDetector} */ - public AccessibilityDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - IMAGE_BUTTON, - IMAGE_VIEW - ); - } - - @Override - @Nullable - public Collection<String> getApplicableAttributes() { - return Collections.singletonList(ATTR_CONTENT_DESCRIPTION); - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - Element element = attribute.getOwnerElement(); - if (element.hasAttributeNS(ANDROID_URI, ATTR_HINT)) { - context.report(ISSUE, element, context.getLocation(attribute), - "Do not set both contentDescription and hint: the contentDescription " + - "will mask the hint", null); - } - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (!element.hasAttributeNS(ANDROID_URI, ATTR_CONTENT_DESCRIPTION)) { - // Ignore views that are explicitly not important for accessibility - if (VALUE_NO.equals(element.getAttributeNS(ANDROID_URI, - ATTR_IMPORTANT_FOR_ACCESSIBILITY))) { - return; - } - context.report(ISSUE, element, context.getLocation(element), - "[Accessibility] Missing contentDescription attribute on image", null); - } else { - Attr attributeNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_CONTENT_DESCRIPTION); - String attribute = attributeNode.getValue(); - if (attribute.isEmpty() || attribute.equals("TODO")) { //$NON-NLS-1$ - context.report(ISSUE, attributeNode, context.getLocation(attributeNode), - "[Accessibility] Empty contentDescription attribute on image", null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/AlwaysShowActionDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/AlwaysShowActionDetector.java deleted file mode 100644 index 34a3f10..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/AlwaysShowActionDetector.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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.ATTR_SHOW_AS_ACTION; -import static com.android.SdkConstants.VALUE_ALWAYS; -import static com.android.SdkConstants.VALUE_IF_ROOM; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector.JavaScanner; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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.Attr; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Node; -import lombok.ast.Select; - -/** - * Check which looks for usage of showAsAction="always" in menus (or - * MenuItem.SHOW_AS_ACTION_ALWAYS in code), which is usually a style guide violation. - * (Use ifRoom instead). - */ -public class AlwaysShowActionDetector extends ResourceXmlDetector implements JavaScanner { - - - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "AlwaysShowAction", //$NON-NLS-1$ - "Checks for uses of showAsAction=\"always\" and suggests showAsAction=\"ifRoom\" " + - "instead", - - "Using `showAsAction=\"always\"` in menu XML, or `MenuItem.SHOW_AS_ACTION_ALWAYS` in " + - "Java code is usually a deviation from the user interface style guide." + - "Use `ifRoom` or the corresponding `MenuItem.SHOW_AS_ACTION_IF_ROOM` instead.\n" + - "\n" + - "If `always` is used sparingly there are usually no problems and behavior is " + - "roughly equivalent to `ifRoom` but with preference over other `ifRoom` " + - "items. Using it more than twice in the same menu is a bad idea.\n" + - "\n" + - "This check looks for menu XML files that contain more than two `always` " + - "actions, or some `always` actions and no `ifRoom` actions. In Java code, " + - "it looks for projects that contain references to `MenuItem.SHOW_AS_ACTION_ALWAYS` " + - "and no references to `MenuItem.SHOW_AS_ACTION_IF_ROOM`.", - - Category.USABILITY, - 3, - Severity.WARNING, - AlwaysShowActionDetector.class, - EnumSet.of(Scope.RESOURCE_FILE, Scope.JAVA_FILE)).setMoreInfo( - "http://developer.android.com/design/patterns/actionbar.html"); //$NON-NLS-1$ - - /** List of showAsAction attributes appearing in the current menu XML file */ - private List<Attr> mFileAttributes; - /** If at least one location has been marked ignore in this file, ignore all */ - private boolean mIgnoreFile; - /** List of locations of MenuItem.SHOW_AS_ACTION_ALWAYS references in Java code */ - private List<Location> mAlwaysFields; - /** True if references to MenuItem.SHOW_AS_ACTION_IF_ROOM were found */ - private boolean mHasIfRoomRefs; - - /** Constructs a new {@link AlwaysShowActionDetector} */ - public AlwaysShowActionDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.MENU; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableAttributes() { - return Collections.singletonList(ATTR_SHOW_AS_ACTION); - } - - @Override - public void beforeCheckFile(@NonNull Context context) { - mFileAttributes = null; - } - - @Override - public void afterCheckFile(@NonNull Context context) { - if (mIgnoreFile) { - mFileAttributes = null; - return; - } - if (mFileAttributes != null) { - assert context instanceof XmlContext; // mFileAttributes is only set in XML files - - List<Attr> always = new ArrayList<Attr>(); - List<Attr> ifRoom = new ArrayList<Attr>(); - for (Attr attribute : mFileAttributes) { - String value = attribute.getValue(); - if (value.equals(VALUE_ALWAYS)) { - always.add(attribute); - } else if (value.equals(VALUE_IF_ROOM)) { - ifRoom.add(attribute); - } else if (value.indexOf('|') != -1) { - String[] flags = value.split("\\|"); //$NON-NLS-1$ - for (String flag : flags) { - if (flag.equals(VALUE_ALWAYS)) { - always.add(attribute); - break; - } else if (flag.equals(VALUE_IF_ROOM)) { - ifRoom.add(attribute); - break; - } - } - } - } - - if (!always.isEmpty() && mFileAttributes.size() > 1) { - // Complain if you're using more than one "always", or if you're - // using "always" and aren't using "ifRoom" at all (and provided you - // have more than a single item) - if (always.size() > 2 || ifRoom.isEmpty()) { - XmlContext xmlContext = (XmlContext) context; - Location location = null; - for (int i = always.size() - 1; i >= 0; i--) { - Location next = location; - location = xmlContext.getLocation(always.get(i)); - if (next != null) { - location.setSecondary(next); - } - } - context.report(ISSUE, location, - "Prefer \"ifRoom\" instead of \"always\"", null); - } - } - } - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (mAlwaysFields != null && !mHasIfRoomRefs) { - for (Location location : mAlwaysFields) { - context.report(ISSUE, location, - "Prefer \"SHOW_AS_ACTION_IF_ROOM\" instead of \"SHOW_AS_ACTION_ALWAYS\"", - null); - } - } - } - - // ---- Implements XmlScanner ---- - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - if (context.getDriver().isSuppressed(ISSUE, attribute)) { - mIgnoreFile = true; - return; - } - - if (mFileAttributes == null) { - mFileAttributes = new ArrayList<Attr>(); - } - mFileAttributes.add(attribute); - } - - // ---- Implements JavaScanner ---- - - @Override - public - List<Class<? extends Node>> getApplicableNodeTypes() { - return Collections.<Class<? extends Node>>singletonList(Select.class); - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - return new FieldAccessChecker(context); - } - - private class FieldAccessChecker extends ForwardingAstVisitor { - private final JavaContext mContext; - - public FieldAccessChecker(JavaContext context) { - mContext = context; - } - - @Override - public boolean visitSelect(Select node) { - String description = node.astIdentifier().getDescription(); - boolean isIfRoom = description.equals("SHOW_AS_ACTION_IF_ROOM"); //$NON-NLS-1$ - boolean isAlways = description.equals("SHOW_AS_ACTION_ALWAYS"); //$NON-NLS-1$ - if ((isIfRoom || isAlways) - && node.astOperand().toString().equals("MenuItem")) { //$NON-NLS-1$ - if (isAlways) { - if (mContext.getDriver().isSuppressed(ISSUE, node)) { - return super.visitSelect(node); - } - if (mAlwaysFields == null) { - mAlwaysFields = new ArrayList<Location>(); - } - mAlwaysFields.add(mContext.getLocation(node)); - } else { - mHasIfRoomRefs = true; - } - } - - return super.visitSelect(node); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java deleted file mode 100644 index eca9256..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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.FQCN_SUPPRESS_LINT; -import static com.android.SdkConstants.SUPPRESS_LINT; - -import com.android.annotations.NonNull; -import com.android.tools.lint.client.api.IssueRegistry; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -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 java.io.File; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import lombok.ast.Annotation; -import lombok.ast.AnnotationElement; -import lombok.ast.AnnotationValue; -import lombok.ast.ArrayInitializer; -import lombok.ast.AstVisitor; -import lombok.ast.Block; -import lombok.ast.ConstructorDeclaration; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.MethodDeclaration; -import lombok.ast.Modifiers; -import lombok.ast.Node; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.StringLiteral; -import lombok.ast.TypeBody; -import lombok.ast.VariableDefinition; -import lombok.ast.VariableDefinitionEntry; - -/** - * Checks annotations to make sure they are valid - */ -public class AnnotationDetector extends Detector implements Detector.JavaScanner { - /** Placing SuppressLint on a local variable doesn't work for class-file based checks */ - public static final Issue ISSUE = Issue.create( - "LocalSuppress", //$NON-NLS-1$ - "Looks for @SuppressLint annotations in locations where it doesn't work for class based checks", - - "The `@SuppressAnnotation` is used to suppress Lint warnings in Java files. However, " + - "while many lint checks analyzes the Java source code, where they can find " + - "annotations on (for example) local variables, some checks are analyzing the " + - "`.class` files. And in class files, annotations only appear on classes, fields " + - "and methods. Annotations placed on local variables disappear. If you attempt " + - "to suppress a lint error for a class-file based lint check, the suppress " + - "annotation not work. You must move the annotation out to the surrounding method.", - - Category.CORRECTNESS, - 3, - Severity.ERROR, - AnnotationDetector.class, - Scope.JAVA_FILE_SCOPE); - - /** Constructs a new {@link AnnotationDetector} check */ - public AnnotationDetector() { - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements JavaScanner ---- - - @Override - public List<Class<? extends Node>> getApplicableNodeTypes() { - return Collections.<Class<? extends Node>>singletonList(Annotation.class); - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - return new AnnotationChecker(context); - } - - private static class AnnotationChecker extends ForwardingAstVisitor { - private final JavaContext mContext; - - public AnnotationChecker(JavaContext context) { - mContext = context; - } - - @Override - public boolean visitAnnotation(Annotation node) { - String type = node.astAnnotationTypeReference().getTypeName(); - if (SUPPRESS_LINT.equals(type) || FQCN_SUPPRESS_LINT.equals(type)) { - Node parent = node.getParent(); - if (parent instanceof Modifiers) { - parent = parent.getParent(); - if (parent instanceof VariableDefinition) { - for (AnnotationElement element : node.astElements()) { - AnnotationValue valueNode = element.astValue(); - if (valueNode == null) { - continue; - } - if (valueNode instanceof StringLiteral) { - StringLiteral literal = (StringLiteral) valueNode; - String id = literal.astValue(); - if (!checkId(node, id)) { - return super.visitAnnotation(node); - } - } else if (valueNode instanceof ArrayInitializer) { - ArrayInitializer array = (ArrayInitializer) valueNode; - StrictListAccessor<Expression, ArrayInitializer> expressions = - array.astExpressions(); - if (expressions == null) { - continue; - } - Iterator<Expression> arrayIterator = expressions.iterator(); - while (arrayIterator.hasNext()) { - Expression arrayElement = arrayIterator.next(); - if (arrayElement instanceof StringLiteral) { - String id = ((StringLiteral) arrayElement).astValue(); - if (!checkId(node, id)) { - return super.visitAnnotation(node); - } - } - } - } - } - } - } - } - - return super.visitAnnotation(node); - } - - private boolean checkId(Annotation node, String id) { - IssueRegistry registry = mContext.getDriver().getRegistry(); - Issue issue = registry.getIssue(id); - // Special-case the ApiDetector issue, since it does both source file analysis - // only on field references, and class file analysis on the rest, so we allow - // annotations outside of methods only on fields - if (issue != null && !issue.getScope().contains(Scope.JAVA_FILE) - || issue == ApiDetector.UNSUPPORTED) { - // Ensure that this isn't a field - Node parent = node.getParent(); - while (parent != null) { - if (parent instanceof MethodDeclaration - || parent instanceof ConstructorDeclaration - || parent instanceof Block) { - break; - } else if (parent instanceof TypeBody) { // It's a field - return true; - } else if (issue == ApiDetector.UNSUPPORTED - && parent instanceof VariableDefinition) { - VariableDefinition definition = (VariableDefinition) parent; - for (VariableDefinitionEntry entry : definition.astVariables()) { - Expression initializer = entry.astInitializer(); - if (initializer instanceof Select) { - return true; - } - } - } - parent = parent.getParent(); - if (parent == null) { - return true; - } - } - - // This issue doesn't have AST access: annotations are not - // available for local variables or parameters - mContext.report(ISSUE, node, mContext.getLocation(node), String.format( - "The @SuppressLint annotation cannot be used on a local " + - "variable with the lint check '%1$s': move out to the " + - "surrounding method", id), - null); - return false; - } - - return true; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/Api.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/Api.java deleted file mode 100644 index ca84b27..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/Api.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 org.xml.sax.SAXException; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -/** - * Main entry point for API description. - * - * To create the {@link Api}, use {@link #parseApi(File)} - * - */ -public class Api { - - /** - * Parses simplified API file. - * @param apiFile the file to read - * @return a new ApiInfo - */ - public static Api parseApi(File apiFile) { - FileInputStream fileInputStream = null; - try { - fileInputStream = new FileInputStream(apiFile); - SAXParserFactory parserFactory = SAXParserFactory.newInstance(); - SAXParser parser = parserFactory.newSAXParser(); - ApiParser apiParser = new ApiParser(); - parser.parse(fileInputStream, apiParser); - return new Api(apiParser.getClasses()); - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } catch (SAXException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (fileInputStream != null) { - try { - fileInputStream.close(); - } catch (IOException e) { - // ignore - } - } - } - - return null; - } - - private final Map<String, ApiClass> mClasses; - - private Api(Map<String, ApiClass> classes) { - mClasses = new HashMap<String, ApiClass>(classes); - } - - ApiClass getClass(String fqcn) { - return mClasses.get(fqcn); - } - - Map<String, ApiClass> getClasses() { - return Collections.unmodifiableMap(mClasses); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiClass.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiClass.java deleted file mode 100644 index 3e0fb9d..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiClass.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * 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 com.android.annotations.Nullable; -import com.android.utils.Pair; -import com.google.common.collect.Lists; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Represents a class and its methods/fields. - * - * {@link #getSince()} gives the API level it was introduced. - * - * {@link #getMethod} returns when the method was introduced. - * {@link #getField} returns when the field was introduced. - */ -public class ApiClass { - - private final String mName; - private final int mSince; - - private final List<Pair<String, Integer>> mSuperClasses = Lists.newArrayList(); - private final List<Pair<String, Integer>> mInterfaces = Lists.newArrayList(); - - private final Map<String, Integer> mFields = new HashMap<String, Integer>(); - private final Map<String, Integer> mMethods = new HashMap<String, Integer>(); - - ApiClass(String name, int since) { - mName = name; - mSince = since; - } - - /** - * Returns the name of the class. - * @return the name of the class - */ - String getName() { - return mName; - } - - /** - * Returns when the class was introduced. - * @return the api level the class was introduced. - */ - int getSince() { - return mSince; - } - - /** - * Returns when a field was added, or null if it doesn't exist. - * @param name the name of the field. - * @param info the corresponding info - */ - Integer getField(String name, Api info) { - // The field can come from this class or from a super class or an interface - // The value can never be lower than this introduction of this class. - // When looking at super classes and interfaces, it can never be lower than when the - // super class or interface was added as a super class or interface to this class. - // Look at all the values and take the lowest. - // For instance: - // This class A is introduced in 5 with super class B. - // In 10, the interface C was added. - // Looking for SOME_FIELD we get the following: - // Present in A in API 15 - // Present in B in API 11 - // Present in C in API 7. - // The answer is 10, which is when C became an interface - int min = Integer.MAX_VALUE; - Integer i = mFields.get(name); - if (i != null) { - min = i; - } - - // now look at the super classes - for (Pair<String, Integer> superClassPair : mSuperClasses) { - ApiClass superClass = info.getClass(superClassPair.getFirst()); - if (superClass != null) { - i = superClass.getField(name, info); - if (i != null) { - int tmp = superClassPair.getSecond() > i ? superClassPair.getSecond() : i; - if (tmp < min) { - min = tmp; - } - } - } - } - - // now look at the interfaces - for (Pair<String, Integer> superClassPair : mInterfaces) { - ApiClass superClass = info.getClass(superClassPair.getFirst()); - if (superClass != null) { - i = superClass.getField(name, info); - if (i != null) { - int tmp = superClassPair.getSecond() > i ? superClassPair.getSecond() : i; - if (tmp < min) { - min = tmp; - } - } - } - } - - return min; - } - - /** - * Returns when a method was added, or null if it doesn't exist. This goes through the super - * class to find method only present there. - * @param methodSignature the method signature - */ - int getMethod(String methodSignature, Api info) { - // The method can come from this class or from a super class. - // The value can never be lower than this introduction of this class. - // When looking at super classes, it can never be lower than when the super class became - // a super class of this class. - // Look at all the values and take the lowest. - // For instance: - // This class A is introduced in 5 with super class B. - // In 10, the super class changes to C. - // Looking for foo() we get the following: - // Present in A in API 15 - // Present in B in API 11 - // Present in C in API 7. - // The answer is 10, which is when C became the super class. - int min = Integer.MAX_VALUE; - Integer i = mMethods.get(methodSignature); - if (i != null) { - min = i; - - // Constructors aren't inherited - if (methodSignature.startsWith(CONSTRUCTOR_NAME)) { - return i; - } - } - - // now look at the super classes - for (Pair<String, Integer> superClassPair : mSuperClasses) { - ApiClass superClass = info.getClass(superClassPair.getFirst()); - if (superClass != null) { - i = superClass.getMethod(methodSignature, info); - if (i != null) { - int tmp = superClassPair.getSecond() > i ? superClassPair.getSecond() : i; - if (tmp < min) { - min = tmp; - } - } - } - } - - // now look at the interfaces classes - for (Pair<String, Integer> interfacePair : mInterfaces) { - ApiClass superClass = info.getClass(interfacePair.getFirst()); - if (superClass != null) { - i = superClass.getMethod(methodSignature, info); - if (i != null) { - int tmp = interfacePair.getSecond() > i ? interfacePair.getSecond() : i; - if (tmp < min) { - min = tmp; - } - } - } - } - - return min; - } - - void addField(String name, int since) { - Integer i = mFields.get(name); - if (i == null || i.intValue() > since) { - mFields.put(name, Integer.valueOf(since)); - } - } - - void addMethod(String name, int since) { - // Strip off the method type at the end to ensure that the code which - // produces inherited methods doesn't get confused and end up multiple entries. - // For example, java/nio/Buffer has the method "array()Ljava/lang/Object;", - // and the subclass java/nio/ByteBuffer has the method "array()[B". We want - // the lookup on mMethods to associate the ByteBuffer array method to be - // considered overriding the Buffer method. - int index = name.indexOf(')'); - if (index != -1) { - name = name.substring(0, index + 1); - } - - Integer i = mMethods.get(name); - if (i == null || i.intValue() > since) { - mMethods.put(name, Integer.valueOf(since)); - } - } - - void addSuperClass(String superClass, int since) { - addToArray(mSuperClasses, superClass, since); - } - - void addInterface(String interfaceClass, int since) { - addToArray(mInterfaces, interfaceClass, since); - } - - void addToArray(List<Pair<String, Integer>> list, String name, int value) { - // check if we already have that name (at a lower level) - for (Pair<String, Integer> pair : list) { - if (name.equals(pair.getFirst())) { - return; - } - } - - list.add(Pair.of(name, Integer.valueOf(value))); - - } - - @Nullable - public String getPackage() { - int index = mName.lastIndexOf('/'); - if (index != -1) { - return mName.substring(0, index); - } - - return null; - } - - @Override - public String toString() { - return mName; - } - - /** - * Returns the set of all methods, including inherited - * ones. - * - * @param info the api to look up super classes from - * @return a set containing all the members fields - */ - Set<String> getAllMethods(Api info) { - Set<String> members = new HashSet<String>(100); - addAllMethods(info, members, true /*includeConstructors*/); - - return members; - } - - private void addAllMethods(Api info, Set<String> set, boolean includeConstructors) { - if (!includeConstructors) { - for (String method : mMethods.keySet()) { - if (!method.startsWith(CONSTRUCTOR_NAME)) { - set.add(method); - } - } - } else { - for (String method : mMethods.keySet()) { - set.add(method); - } - } - - for (Pair<String, Integer> superClass : mSuperClasses) { - ApiClass clz = info.getClass(superClass.getFirst()); - assert clz != null : superClass.getSecond(); - if (clz != null) { - clz.addAllMethods(info, set, false); - } - } - - // Get methods from implemented interfaces as well; - for (Pair<String, Integer> superClass : mInterfaces) { - ApiClass clz = info.getClass(superClass.getFirst()); - assert clz != null : superClass.getSecond(); - if (clz != null) { - clz.addAllMethods(info, set, false); - } - } - } - - /** - * Returns the set of all fields, including inherited - * ones. - * - * @param info the api to look up super classes from - * @return a set containing all the fields - */ - Set<String> getAllFields(Api info) { - Set<String> members = new HashSet<String>(100); - addAllFields(info, members); - - return members; - } - - private void addAllFields(Api info, Set<String> set) { - for (String field : mFields.keySet()) { - set.add(field); - } - - for (Pair<String, Integer> superClass : mSuperClasses) { - ApiClass clz = info.getClass(superClass.getFirst()); - assert clz != null : superClass.getSecond(); - if (clz != null) { - clz.addAllFields(info, set); - } - } - - // Get methods from implemented interfaces as well; - for (Pair<String, Integer> superClass : mInterfaces) { - ApiClass clz = info.getClass(superClass.getFirst()); - assert clz != null : superClass.getSecond(); - if (clz != null) { - clz.addAllFields(info, set); - } - } - } - - /* This code can be used to scan through all the fields and look for fields - that have moved to a higher class: - Field android/view/MotionEvent#CREATOR has api=1 but parent android/view/InputEvent provides it as 9 - Field android/provider/ContactsContract$CommonDataKinds$Organization#PHONETIC_NAME has api=5 but parent android/provider/ContactsContract$ContactNameColumns provides it as 11 - Field android/widget/ListView#CHOICE_MODE_MULTIPLE has api=1 but parent android/widget/AbsListView provides it as 11 - Field android/widget/ListView#CHOICE_MODE_NONE has api=1 but parent android/widget/AbsListView provides it as 11 - Field android/widget/ListView#CHOICE_MODE_SINGLE has api=1 but parent android/widget/AbsListView provides it as 11 - Field android/view/KeyEvent#CREATOR has api=1 but parent android/view/InputEvent provides it as 9 - This is used for example in the ApiDetector to filter out warnings which result - when people follow Eclipse's advice to replace - ListView.CHOICE_MODE_MULTIPLE - references with - AbsListView.CHOICE_MODE_MULTIPLE - since the latter has API=11 and the former has API=1; since the constant is unchanged - between the two, and the literal is copied into the class, using the AbsListView - reference works. - public void checkFields(Api info) { - fieldLoop: - for (String field : mFields.keySet()) { - Integer since = getField(field, info); - if (since == null || since == Integer.MAX_VALUE) { - continue; - } - - for (Pair<String, Integer> superClass : mSuperClasses) { - ApiClass clz = info.getClass(superClass.getFirst()); - assert clz != null : superClass.getSecond(); - if (clz != null) { - Integer superSince = clz.getField(field, info); - if (superSince == Integer.MAX_VALUE) { - continue; - } - - if (superSince != null && superSince > since) { - String declaredIn = clz.findFieldDeclaration(info, field); - System.out.println("Field " + getName() + "#" + field + " has api=" - + since + " but parent " + declaredIn + " provides it as " - + superSince); - continue fieldLoop; - } - } - } - - // Get methods from implemented interfaces as well; - for (Pair<String, Integer> superClass : mInterfaces) { - ApiClass clz = info.getClass(superClass.getFirst()); - assert clz != null : superClass.getSecond(); - if (clz != null) { - Integer superSince = clz.getField(field, info); - if (superSince == Integer.MAX_VALUE) { - continue; - } - if (superSince != null && superSince > since) { - String declaredIn = clz.findFieldDeclaration(info, field); - System.out.println("Field " + getName() + "#" + field + " has api=" - + since + " but parent " + declaredIn + " provides it as " - + superSince); - continue fieldLoop; - } - } - } - } - } - - private String findFieldDeclaration(Api info, String name) { - if (mFields.containsKey(name)) { - return getName(); - } - for (Pair<String, Integer> superClass : mSuperClasses) { - ApiClass clz = info.getClass(superClass.getFirst()); - assert clz != null : superClass.getSecond(); - if (clz != null) { - String declaredIn = clz.findFieldDeclaration(info, name); - if (declaredIn != null) { - return declaredIn; - } - } - } - - // Get methods from implemented interfaces as well; - for (Pair<String, Integer> superClass : mInterfaces) { - ApiClass clz = info.getClass(superClass.getFirst()); - assert clz != null : superClass.getSecond(); - if (clz != null) { - String declaredIn = clz.findFieldDeclaration(info, name); - if (declaredIn != null) { - return declaredIn; - } - } - } - - return null; - } - */ -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java deleted file mode 100644 index 146e9e1..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java +++ /dev/null @@ -1,1562 +0,0 @@ -/* - * 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.ANDROID_PREFIX; -import static com.android.SdkConstants.ANDROID_THEME_PREFIX; -import static com.android.SdkConstants.ATTR_CLASS; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_TARGET_API; -import static com.android.SdkConstants.CONSTRUCTOR_NAME; -import static com.android.SdkConstants.PREFIX_ANDROID; -import static com.android.SdkConstants.R_CLASS; -import static com.android.SdkConstants.TAG_ITEM; -import static com.android.SdkConstants.TAG_STYLE; -import static com.android.SdkConstants.TARGET_API; -import static com.android.SdkConstants.TOOLS_URI; -import static com.android.SdkConstants.VIEW_TAG; -import static com.android.tools.lint.detector.api.ClassContext.getFqcn; -import static com.android.tools.lint.detector.api.ClassContext.getInternalName; -import static com.android.tools.lint.detector.api.LintUtils.getNextInstruction; -import static com.android.tools.lint.detector.api.Location.SearchDirection.BACKWARD; -import static com.android.tools.lint.detector.api.Location.SearchDirection.FORWARD; -import static com.android.tools.lint.detector.api.Location.SearchDirection.NEAREST; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -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.DefaultPosition; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Location.SearchHints; -import com.android.tools.lint.detector.api.Position; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 com.android.utils.Pair; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.AnnotationNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.IntInsnNode; -import org.objectweb.asm.tree.LdcInsnNode; -import org.objectweb.asm.tree.LocalVariableNode; -import org.objectweb.asm.tree.LookupSwitchInsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import lombok.ast.Annotation; -import lombok.ast.AnnotationElement; -import lombok.ast.AnnotationValue; -import lombok.ast.AstVisitor; -import lombok.ast.BinaryExpression; -import lombok.ast.Case; -import lombok.ast.ClassDeclaration; -import lombok.ast.ConstructorDeclaration; -import lombok.ast.ConstructorInvocation; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.If; -import lombok.ast.ImportDeclaration; -import lombok.ast.InlineIfExpression; -import lombok.ast.IntegralLiteral; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Modifiers; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.StringLiteral; -import lombok.ast.SuperConstructorInvocation; -import lombok.ast.Switch; -import lombok.ast.TypeReference; -import lombok.ast.VariableDefinition; -import lombok.ast.VariableDefinitionEntry; -import lombok.ast.VariableReference; - -/** - * Looks for usages of APIs that are not supported in all the versions targeted - * by this application (according to its minimum API requirement in the manifest). - */ -public class ApiDetector extends ResourceXmlDetector - implements Detector.ClassScanner, Detector.JavaScanner { - /** - * Whether we flag variable, field, parameter and return type declarations of a type - * not yet available. It appears Dalvik is very forgiving and doesn't try to preload - * classes until actually needed, so there is no need to flag these, and in fact, - * patterns used for supporting new and old versions sometimes declares these methods - * and only conditionally end up actually accessing methods and fields, so only check - * method and field accesses. - */ - private static final boolean CHECK_DECLARATIONS = false; - - private static final boolean AOSP_BUILD = System.getenv("ANDROID_BUILD_TOP") != null; //$NON-NLS-1$ - - /** Accessing an unsupported API */ - public static final Issue UNSUPPORTED = Issue.create("NewApi", //$NON-NLS-1$ - "Finds API accesses to APIs that are not supported in all targeted API versions", - - "This check scans through all the Android API calls in the application and " + - "warns about any calls that are not available on *all* versions targeted " + - "by this application (according to its minimum SDK attribute in the manifest).\n" - + - "\n" + - "If you really want to use this API and don't need to support older devices just " - + - "set the `minSdkVersion` in your `AndroidManifest.xml` file." + - "\n" + - "If your code is *deliberately* accessing newer APIs, and you have ensured " + - "(e.g. with conditional execution) that this code will only ever be called on a " - + - "supported platform, then you can annotate your class or method with the " + - "`@TargetApi` annotation specifying the local minimum SDK to apply, such as " + - "`@TargetApi(11)`, such that this check considers 11 rather than your manifest " - + - "file's minimum SDK as the required API level.\n" + - "\n" + - "If you are deliberately setting `android:` attributes in style definitions, " + - "make sure you place this in a `values-v11` folder in order to avoid running " + - "into runtime conflicts on certain devices where manufacturers have added " + - "custom attributes whose ids conflict with the new ones on later platforms.\n" + - "\n" + - "Similarly, you can use tools:targetApi=\"11\" in an XML file to indicate that " - + - "the element will only be inflated in an adequate context.", - Category.CORRECTNESS, - 6, - Severity.ERROR, - ApiDetector.class, - EnumSet.of(Scope.CLASS_FILE, Scope.RESOURCE_FILE, Scope.MANIFEST)) - .addAnalysisScope(Scope.RESOURCE_FILE_SCOPE) - .addAnalysisScope(Scope.CLASS_FILE_SCOPE); - - /** Accessing an inlined API on older platforms */ - public static final Issue INLINED = Issue.create("InlinedApi", //$NON-NLS-1$ - "Finds inlined fields that may or may not work on older platforms", - - "This check scans through all the Android API field references in the application " + - "and flags certain constants, such as static final integers and Strings, " + - "which were introduced in later versions. These will actually be copied " + - "into the class files rather than being referenced, which means that " + - "the value is available even when running on older devices. In some " + - "cases that's fine, and in other cases it can result in a runtime " + - "crash or incorrect behavior. It depends on the context, so consider " + - "the code carefully and device whether it's safe and can be suppressed " + - "or whether the code needs tbe guarded.\n" + - "\n" + - "If you really want to use this API and don't need to support older devices just " - + - "set the `minSdkVersion` in your `AndroidManifest.xml` file." + - "\n" + - "If your code is *deliberately* accessing newer APIs, and you have ensured " + - "(e.g. with conditional execution) that this code will only ever be called on a " - + - "supported platform, then you can annotate your class or method with the " + - "`@TargetApi` annotation specifying the local minimum SDK to apply, such as " + - "`@TargetApi(11)`, such that this check considers 11 rather than your manifest " - + - "file's minimum SDK as the required API level.\n", - Category.CORRECTNESS, - 6, - Severity.WARNING, - ApiDetector.class, - EnumSet.of(Scope.JAVA_FILE)) - .addAnalysisScope(Scope.JAVA_FILE_SCOPE); - - /** Accessing an unsupported API */ - public static final Issue OVERRIDE = Issue.create("Override", //$NON-NLS-1$ - "Finds method declarations that will accidentally override methods in later versions", - - "Suppose you are building against Android API 8, and you've subclassed Activity. " + - "In your subclass you add a new method called `isDestroyed`(). At some later point, " + - "a method of the same name and signature is added to Android. Your method will " + - "now override the Android method, and possibly break its contract. Your method " + - "is not calling `super.isDestroyed()`, since your compilation target doesn't " + - "know about the method.\n" + - "\n" + - "The above scenario is what this lint detector looks for. The above example is " + - "real, since `isDestroyed()` was added in API 17, but it will be true for *any* " + - "method you have added to a subclass of an Android class where your build target " + - "is lower than the version the method was introduced in.\n" + - "\n" + - "To fix this, either rename your method, or if you are really trying to augment " + - "the builtin method if available, switch to a higher build target where you can " + - "deliberately add `@Override` on your overriding method, and call `super` if " + - "appropriate etc.\n", - Category.CORRECTNESS, - 6, - Severity.ERROR, - ApiDetector.class, - Scope.CLASS_FILE_SCOPE); - - private static final String TARGET_API_VMSIG = '/' + TARGET_API + ';'; - private static final String SWITCH_TABLE_PREFIX = "$SWITCH_TABLE$"; //$NON-NLS-1$ - private static final String ORDINAL_METHOD = "ordinal"; //$NON-NLS-1$ - - protected ApiLookup mApiDatabase; - private int mMinApi = -1; - private Map<String, List<Pair<String, Location>>> mPendingFields; - - /** Constructs a new API check */ - public ApiDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.SLOW; - } - - @Override - public void beforeCheckProject(@NonNull Context context) { - mApiDatabase = ApiLookup.get(context.getClient()); - // We can't look up the minimum API required by the project here: - // The manifest file hasn't been processed yet in the -before- project hook. - // For now it's initialized lazily in getMinSdk(Context), but the - // lint infrastructure should be fixed to parse manifest file up front. - } - - // ---- Implements XmlScanner ---- - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return true; - } - - @Override - public Collection<String> getApplicableElements() { - return ALL; - } - - @Override - public Collection<String> getApplicableAttributes() { - return ALL; - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - if (mApiDatabase == null) { - return; - } - - String value = attribute.getValue(); - - String owner = null; - String name = null; - - String prefix; - if (value.startsWith(ANDROID_PREFIX)) { - prefix = ANDROID_PREFIX; - } else if (value.startsWith(ANDROID_THEME_PREFIX)) { - prefix = ANDROID_THEME_PREFIX; - } else if (value.startsWith(PREFIX_ANDROID) && ATTR_NAME.equals(attribute.getName()) - && TAG_ITEM.equals(attribute.getOwnerElement().getTagName()) - && attribute.getOwnerElement().getParentNode() != null - && TAG_STYLE.equals(attribute.getOwnerElement().getParentNode().getNodeName())) { - owner = "android/R$attr"; //$NON-NLS-1$ - name = value.substring(PREFIX_ANDROID.length()); - prefix = PREFIX_ANDROID; - } else { - return; - } - - if (owner == null) { - // Convert @android:type/foo into android/R$type and "foo" - int index = value.indexOf('/', prefix.length()); - if (index != -1) { - owner = "android/R$" //$NON-NLS-1$ - + value.substring(prefix.length(), index); - name = value.substring(index + 1); - if (name.indexOf('.') != -1) { - name = name.replace('.', '_'); - } - } else if (value.startsWith(ANDROID_THEME_PREFIX)) { - owner = "android/R$attr"; //$NON-NLS-1$ - name = value.substring(ANDROID_THEME_PREFIX.length()); - } else { - return; - } - } - assert name != null; // Eclipse can't infer this - int api = mApiDatabase.getFieldVersion(owner, name); - int minSdk = getMinSdk(context); - if (api > minSdk && api > context.getFolderVersion() - && api > getLocalMinSdk(attribute.getOwnerElement())) { - // Don't complain about resource references in the tools namespace, - // such as for example "tools:layout="@android:layout/list_content", - // used only for designtime previews - if (TOOLS_URI.equals(attribute.getNamespaceURI())) { - return; - } - - Location location = context.getLocation(attribute); - String message = String.format( - "%1$s requires API level %2$d (current min is %3$d)", - value, api, minSdk); - context.report(UNSUPPORTED, attribute, location, message, null); - } - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (mApiDatabase == null) { - return; - } - - String tag = element.getTagName(); - - ResourceFolderType folderType = context.getResourceFolderType(); - if (folderType != ResourceFolderType.LAYOUT) { - if (element.getParentNode().getNodeType() != Node.ELEMENT_NODE) { - // Root node - return; - } - NodeList childNodes = element.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node textNode = childNodes.item(i); - if (textNode.getNodeType() == Node.TEXT_NODE) { - String text = textNode.getNodeValue(); - if (text.indexOf(ANDROID_PREFIX) != -1) { - text = text.trim(); - // Convert @android:type/foo into android/R$type and "foo" - int index = text.indexOf('/', ANDROID_PREFIX.length()); - if (index != -1) { - String owner = "android/R$" //$NON-NLS-1$ - + text.substring(ANDROID_PREFIX.length(), index); - String name = text.substring(index + 1); - if (name.indexOf('.') != -1) { - name = name.replace('.', '_'); - } - int api = mApiDatabase.getFieldVersion(owner, name); - int minSdk = getMinSdk(context); - if (api > minSdk && api > context.getFolderVersion() - && api > getLocalMinSdk(element)) { - Location location = context.getLocation(textNode); - String message = String.format( - "%1$s requires API level %2$d (current min is %3$d)", - text, api, minSdk); - context.report(UNSUPPORTED, element, location, message, null); - } - } - } - } - } - } else if (folderType == ResourceFolderType.LAYOUT) { - if (VIEW_TAG.equals(tag)) { - tag = element.getAttribute(ATTR_CLASS); - if (tag == null || tag.isEmpty()) { - return; - } - } - - // Check widgets to make sure they're available in this version of the SDK. - if (tag.indexOf('.') != -1 || - folderType != ResourceFolderType.LAYOUT) { - // Custom views aren't in the index - return; - } - // TODO: Consider other widgets outside of android.widget.* - int api = mApiDatabase.getCallVersion("android/widget/" + tag, //$NON-NLS-1$ - CONSTRUCTOR_NAME, - // Not all views provided this constructor right away, for example, - // LinearLayout added it in API 11 yet LinearLayout is much older: - // "(Landroid/content/Context;Landroid/util/AttributeSet;I)V"); //$NON-NLS-1$ - "(Landroid/content/Context;)"); //$NON-NLS-1$ - int minSdk = getMinSdk(context); - if (api > minSdk && api > context.getFolderVersion() - && api > getLocalMinSdk(element)) { - Location location = context.getLocation(element); - String message = String.format( - "View requires API level %1$d (current min is %2$d): <%3$s>", - api, minSdk, tag); - context.report(UNSUPPORTED, element, location, message, null); - } - } - } - - protected int getMinSdk(Context context) { - if (mMinApi == -1) { - mMinApi = context.getMainProject().getMinSdk(); - } - - return mMinApi; - } - - // ---- Implements ClassScanner ---- - - @SuppressWarnings("rawtypes") // ASM API - @Override - public void checkClass(@NonNull final ClassContext context, @NonNull ClassNode classNode) { - if (mApiDatabase == null) { - return; - } - - if (AOSP_BUILD && classNode.name.startsWith("android/support/")) { //$NON-NLS-1$ - return; - } - - // Requires util package (add prebuilts/tools/common/asm-tools/asm-debug-all-4.0.jar) - //classNode.accept(new TraceClassVisitor(new PrintWriter(System.out))); - - int classMinSdk = getClassMinSdk(context, classNode); - if (classMinSdk == -1) { - classMinSdk = getMinSdk(context); - } - - List methodList = classNode.methods; - if (methodList.isEmpty()) { - return; - } - - boolean checkCalls = context.isEnabled(UNSUPPORTED) - || context.isEnabled(INLINED); - boolean checkMethods = context.isEnabled(OVERRIDE) - && context.getMainProject().getBuildSdk() >= 1; - String frameworkParent = null; - if (checkMethods) { - LintDriver driver = context.getDriver(); - String owner = classNode.superName; - while (owner != null) { - // For virtual dispatch, walk up the inheritance chain checking - // each inherited method - if (owner.startsWith("android/") //$NON-NLS-1$ - || owner.startsWith("java/") //$NON-NLS-1$ - || owner.startsWith("javax/")) { //$NON-NLS-1$ - frameworkParent = owner; - break; - } - owner = driver.getSuperClass(owner); - } - if (frameworkParent == null) { - checkMethods = false; - } - } - - for (Object m : methodList) { - MethodNode method = (MethodNode) m; - - int minSdk = getLocalMinSdk(method.invisibleAnnotations); - if (minSdk == -1) { - minSdk = classMinSdk; - } - - InsnList nodes = method.instructions; - - if (checkMethods && Character.isJavaIdentifierStart(method.name.charAt(0))) { - int buildSdk = context.getMainProject().getBuildSdk(); - String name = method.name; - assert frameworkParent != null; - int api = mApiDatabase.getCallVersion(frameworkParent, name, method.desc); - if (api > buildSdk && buildSdk != -1) { - // TODO: Don't complain if it's annotated with @Override; that means - // somehow the build target isn't correct. - String fqcn; - String owner = classNode.name; - if (CONSTRUCTOR_NAME.equals(name)) { - fqcn = "new " + ClassContext.getFqcn(owner); //$NON-NLS-1$ - } else { - fqcn = ClassContext.getFqcn(owner) + '#' + name; - } - String message = String.format( - "This method is not overriding anything with the current build " + - "target, but will in API level %1$d (current target is %2$d): %3$s", - api, buildSdk, fqcn); - - Location location = context.getLocation(method, classNode); - context.report(OVERRIDE, method, null, location, message, null); - } - } - - if (!checkCalls) { - continue; - } - - if (CHECK_DECLARATIONS) { - // Check types in parameter list and types of local variables - List localVariables = method.localVariables; - if (localVariables != null) { - for (Object v : localVariables) { - LocalVariableNode var = (LocalVariableNode) v; - String desc = var.desc; - if (desc.charAt(0) == 'L') { - // "Lpackage/Class;" => "package/Bar" - String className = desc.substring(1, desc.length() - 1); - int api = mApiDatabase.getClassVersion(className); - if (api > minSdk) { - String fqcn = ClassContext.getFqcn(className); - String message = String.format( - "Class requires API level %1$d (current min is %2$d): %3$s", - api, minSdk, fqcn); - report(context, message, var.start, method, - className.substring(className.lastIndexOf('/') + 1), null, - SearchHints.create(NEAREST).matchJavaSymbol()); - } - } - } - } - - // Check return type - // The parameter types are already handled as local variables so we can skip - // right to the return type. - // Check types in parameter list - String signature = method.desc; - if (signature != null) { - int args = signature.indexOf(')'); - if (args != -1 && signature.charAt(args + 1) == 'L') { - String type = signature.substring(args + 2, signature.length() - 1); - int api = mApiDatabase.getClassVersion(type); - if (api > minSdk) { - String fqcn = ClassContext.getFqcn(type); - String message = String.format( - "Class requires API level %1$d (current min is %2$d): %3$s", - api, minSdk, fqcn); - AbstractInsnNode first = nodes.size() > 0 ? nodes.get(0) : null; - report(context, message, first, method, method.name, null, - SearchHints.create(BACKWARD).matchJavaSymbol()); - } - } - } - } - - for (int i = 0, n = nodes.size(); i < n; i++) { - AbstractInsnNode instruction = nodes.get(i); - int type = instruction.getType(); - if (type == AbstractInsnNode.METHOD_INSN) { - MethodInsnNode node = (MethodInsnNode) instruction; - String name = node.name; - String owner = node.owner; - String desc = node.desc; - - // No need to check methods in this local class; we know they - // won't be an API match - if (node.getOpcode() == Opcodes.INVOKEVIRTUAL - && owner.equals(classNode.name)) { - owner = classNode.superName; - } - - boolean checkingSuperClass = false; - while (owner != null) { - int api = mApiDatabase.getCallVersion(owner, name, desc); - if (api > minSdk) { - if (method.name.startsWith(SWITCH_TABLE_PREFIX)) { - // We're in a compiler-generated method to generate an - // array indexed by enum ordinal values to enum values. The enum - // itself must be requiring a higher API number than is - // currently used, but the call site for the switch statement - // will also be referencing it, so no need to report these - // calls. - break; - } - - if (!checkingSuperClass - && node.getOpcode() == Opcodes.INVOKEVIRTUAL - && methodDefinedLocally(classNode, name, desc)) { - break; - } - - String fqcn; - if (CONSTRUCTOR_NAME.equals(name)) { - fqcn = "new " + ClassContext.getFqcn(owner); //$NON-NLS-1$ - } else { - fqcn = ClassContext.getFqcn(owner) + '#' + name; - } - String message = String.format( - "Call requires API level %1$d (current min is %2$d): %3$s", - api, minSdk, fqcn); - - if (name.equals(ORDINAL_METHOD) - && instruction.getNext() != null - && instruction.getNext().getNext() != null - && instruction.getNext().getOpcode() == Opcodes.IALOAD - && instruction.getNext().getNext().getOpcode() - == Opcodes.TABLESWITCH) { - message = String.format( - "Enum for switch requires API level %1$d " + - "(current min is %2$d): %3$s", - api, minSdk, ClassContext.getFqcn(owner)); - } - - report(context, message, node, method, name, null, - SearchHints.create(FORWARD).matchJavaSymbol()); - } - - // For virtual dispatch, walk up the inheritance chain checking - // each inherited method - if (owner.startsWith("android/") //$NON-NLS-1$ - || owner.startsWith("javax/")) { //$NON-NLS-1$ - // The API map has already inlined all inherited methods - // so no need to keep checking up the chain - owner = null; - } else if (owner.startsWith("java/")) { //$NON-NLS-1$ - if (owner.equals(LocaleDetector.DATE_FORMAT_OWNER)) { - checkSimpleDateFormat(context, method, node, minSdk); - } - // Already inlined; see comment above - owner = null; - } else if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) { - owner = context.getDriver().getSuperClass(owner); - } else if (node.getOpcode() == Opcodes.INVOKESTATIC && api == -1) { - // Inherit through static classes as well - owner = context.getDriver().getSuperClass(owner); - } else { - owner = null; - } - - checkingSuperClass = true; - } - } else if (type == AbstractInsnNode.FIELD_INSN) { - FieldInsnNode node = (FieldInsnNode) instruction; - String name = node.name; - String owner = node.owner; - int api = mApiDatabase.getFieldVersion(owner, name); - if (api > minSdk) { - if (method.name.startsWith(SWITCH_TABLE_PREFIX)) { - checkSwitchBlock(context, classNode, node, method, name, owner, - api, minSdk); - continue; - } - String fqcn = ClassContext.getFqcn(owner) + '#' + name; - if (mPendingFields != null) { - mPendingFields.remove(fqcn); - } - String message = String.format( - "Field requires API level %1$d (current min is %2$d): %3$s", - api, minSdk, fqcn); - report(context, message, node, method, name, null, - SearchHints.create(FORWARD).matchJavaSymbol()); - } - } else if (type == AbstractInsnNode.LDC_INSN) { - LdcInsnNode node = (LdcInsnNode) instruction; - if (node.cst instanceof Type) { - Type t = (Type) node.cst; - String className = t.getInternalName(); - - int api = mApiDatabase.getClassVersion(className); - if (api > minSdk) { - String fqcn = ClassContext.getFqcn(className); - String message = String.format( - "Class requires API level %1$d (current min is %2$d): %3$s", - api, minSdk, fqcn); - report(context, message, node, method, - className.substring(className.lastIndexOf('/') + 1), null, - SearchHints.create(FORWARD).matchJavaSymbol()); - } - } - } - } - } - } - - private static void checkSimpleDateFormat(ClassContext context, MethodNode method, - MethodInsnNode node, int minSdk) { - if (minSdk >= 9) { - // Already OK - return; - } - if (node.name.equals(CONSTRUCTOR_NAME) && !node.desc.equals("()V")) { //$NON-NLS-1$ - // Check first argument - AbstractInsnNode prev = LintUtils.getPrevInstruction(node); - if (prev != null && !node.desc.equals("(Ljava/lang/String;)V")) { //$NON-NLS-1$ - prev = LintUtils.getPrevInstruction(prev); - } - if (prev != null && prev.getOpcode() == Opcodes.LDC) { - LdcInsnNode ldc = (LdcInsnNode) prev; - Object cst = ldc.cst; - if (cst instanceof String) { - String pattern = (String) cst; - boolean isEscaped = false; - for (int i = 0; i < pattern.length(); i++) { - char c = pattern.charAt(i); - if (c == '\'') { - isEscaped = !isEscaped; - } else if (!isEscaped && (c == 'L' || c == 'c')) { - String message = String.format( - "The pattern character '%1$c' requires API level 9 (current " + - "min is %2$d) : \"%3$s\"", c, minSdk, pattern); - report(context, message, node, method, pattern, null, - SearchHints.create(FORWARD)); - return; - } - } - } - } - } - } - - @SuppressWarnings("rawtypes") // ASM API - private static boolean methodDefinedLocally(ClassNode classNode, String name, String desc) { - List methodList = classNode.methods; - for (Object m : methodList) { - MethodNode method = (MethodNode) m; - if (name.equals(method.name) && desc.equals(method.desc)) { - return true; - } - } - - return false; - } - - @SuppressWarnings("rawtypes") // ASM API - private static void checkSwitchBlock(ClassContext context, ClassNode classNode, - FieldInsnNode field, MethodNode method, String name, String owner, int api, - int minSdk) { - // Switch statements on enums are tricky. The compiler will generate a method - // which returns an array of the enum constants, indexed by their ordinal() values. - // However, we only want to complain if the code is actually referencing one of - // the non-available enum fields. - // - // For the android.graphics.PorterDuff.Mode enum for example, the first few items - // in the array are populated like this: - // - // L0 - // ALOAD 0 - // GETSTATIC android/graphics/PorterDuff$Mode.ADD : Landroid/graphics/PorterDuff$Mode; - // INVOKEVIRTUAL android/graphics/PorterDuff$Mode.ordinal ()I - // ICONST_1 - // IASTORE - // L1 - // GOTO L3 - // L2 - // FRAME FULL [[I] [java/lang/NoSuchFieldError] - // POP - // L3 - // FRAME SAME - // ALOAD 0 - // GETSTATIC android/graphics/PorterDuff$Mode.CLEAR : Landroid/graphics/PorterDuff$Mode; - // INVOKEVIRTUAL android/graphics/PorterDuff$Mode.ordinal ()I - // ICONST_2 - // IASTORE - // ... - // So if we for example find that the "ADD" field isn't accessible, since it requires - // API 11, we need to - // (1) First find out what its ordinal number is. We can look at the following - // instructions to discover this; it's the "ICONST_1" and "IASTORE" instructions. - // (After ICONST_5 it moves on to BIPUSH 6, BIPUSH 7, etc.) - // (2) Find the corresponding *usage* of this switch method. For the above enum, - // the switch ordinal lookup method will be called - // "$SWITCH_TABLE$android$graphics$PorterDuff$Mode" with desc "()[I". - // This means we will be looking for an invocation in some other method which looks - // like this: - // INVOKESTATIC (current class).$SWITCH_TABLE$android$graphics$PorterDuff$Mode ()[I - // (obviously, it can be invoked more than once) - // Note that it can be used more than once in this class and all sites should be - // checked! - // (3) Look up the corresponding table switch, which should look something like this: - // INVOKESTATIC (current class).$SWITCH_TABLE$android$graphics$PorterDuff$Mode ()[I - // ALOAD 0 - // INVOKEVIRTUAL android/graphics/PorterDuff$Mode.ordinal ()I - // IALOAD - // LOOKUPSWITCH - // 2: L1 - // 11: L2 - // default: L3 - // Here we need to see if the LOOKUPSWITCH instruction is referencing our target - // case. Above we were looking for the "ADD" case which had ordinal 1. Since this - // isn't explicitly referenced, we can ignore this field reference. - AbstractInsnNode next = field.getNext(); - if (next == null || next.getOpcode() != Opcodes.INVOKEVIRTUAL) { - return; - } - next = next.getNext(); - if (next == null) { - return; - } - int ordinal; - switch (next.getOpcode()) { - case Opcodes.ICONST_0: ordinal = 0; break; - case Opcodes.ICONST_1: ordinal = 1; break; - case Opcodes.ICONST_2: ordinal = 2; break; - case Opcodes.ICONST_3: ordinal = 3; break; - case Opcodes.ICONST_4: ordinal = 4; break; - case Opcodes.ICONST_5: ordinal = 5; break; - case Opcodes.BIPUSH: { - IntInsnNode iin = (IntInsnNode) next; - ordinal = iin.operand; - break; - } - default: - return; - } - - // Find usages of this call site - List methodList = classNode.methods; - for (Object m : methodList) { - InsnList nodes = ((MethodNode) m).instructions; - for (int i = 0, n = nodes.size(); i < n; i++) { - AbstractInsnNode instruction = nodes.get(i); - if (instruction.getOpcode() != Opcodes.INVOKESTATIC){ - continue; - } - MethodInsnNode node = (MethodInsnNode) instruction; - if (node.name.equals(method.name) - && node.desc.equals(method.desc) - && node.owner.equals(classNode.name)) { - // Find lookup switch - AbstractInsnNode target = getNextInstruction(node); - while (target != null) { - if (target.getOpcode() == Opcodes.LOOKUPSWITCH) { - LookupSwitchInsnNode lookup = (LookupSwitchInsnNode) target; - @SuppressWarnings("unchecked") // ASM API - List<Integer> keys = lookup.keys; - if (keys != null && keys.contains(ordinal)) { - String fqcn = ClassContext.getFqcn(owner) + '#' + name; - String message = String.format( - "Enum value requires API level %1$d " + - "(current min is %2$d): %3$s", - api, minSdk, fqcn); - report(context, message, lookup, (MethodNode) m, name, null, - SearchHints.create(FORWARD).matchJavaSymbol()); - - // Break out of the inner target search only; the switch - // statement could be used in other places in this class as - // well and we want to report all problematic usages. - break; - } - } - target = getNextInstruction(target); - } - } - } - } - } - - /** - * Return the {@code @TargetApi} level to use for the given {@code classNode}; - * this will be the {@code @TargetApi} annotation on the class, or any outer - * methods (for anonymous inner classes) or outer classes (for inner classes) - * of the given class. - */ - private static int getClassMinSdk(ClassContext context, ClassNode classNode) { - int classMinSdk = getLocalMinSdk(classNode.invisibleAnnotations); - if (classMinSdk != -1) { - return classMinSdk; - } - - LintDriver driver = context.getDriver(); - while (classNode != null) { - ClassNode prev = classNode; - classNode = driver.getOuterClassNode(classNode); - if (classNode != null) { - // TODO: Should this be "curr" instead? - if (prev.outerMethod != null) { - @SuppressWarnings("rawtypes") // ASM API - List methods = classNode.methods; - for (Object m : methods) { - MethodNode method = (MethodNode) m; - if (method.name.equals(prev.outerMethod) - && method.desc.equals(prev.outerMethodDesc)) { - // Found the outer method for this anonymous class; check method - // annotations on it, then continue up the class hierarchy - int methodMinSdk = getLocalMinSdk(method.invisibleAnnotations); - if (methodMinSdk != -1) { - return methodMinSdk; - } - - break; - } - } - } - - classMinSdk = getLocalMinSdk(classNode.invisibleAnnotations); - if (classMinSdk != -1) { - return classMinSdk; - } - } - } - - return -1; - } - - /** - * Returns the minimum SDK to use according to the given annotation list, or - * -1 if no annotation was found. - * - * @param annotations a list of annotation nodes from ASM - * @return the API level to use for this node, or -1 - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - private static int getLocalMinSdk(List annotations) { - if (annotations != null) { - for (AnnotationNode annotation : (List<AnnotationNode>)annotations) { - String desc = annotation.desc; - if (desc.endsWith(TARGET_API_VMSIG)) { - if (annotation.values != null) { - for (int i = 0, n = annotation.values.size(); i < n; i += 2) { - String key = (String) annotation.values.get(i); - if (key.equals("value")) { //$NON-NLS-1$ - Object value = annotation.values.get(i + 1); - if (value instanceof Integer) { - return ((Integer) value).intValue(); - } - } - } - } - } - } - } - - return -1; - } - - /** - * Returns the minimum SDK to use in the given element context, or -1 if no - * {@code tools:targetApi} attribute was found. - * - * @param element the element to look at, including parents - * @return the API level to use for this element, or -1 - */ - private static int getLocalMinSdk(@NonNull Element element) { - while (element != null) { - String targetApi = element.getAttributeNS(TOOLS_URI, ATTR_TARGET_API); - if (targetApi != null && !targetApi.isEmpty()) { - if (Character.isDigit(targetApi.charAt(0))) { - try { - return Integer.parseInt(targetApi); - } catch (NumberFormatException nufe) { - break; - } - } - - for (int api = 1; api <= SdkConstants.HIGHEST_KNOWN_API; api++) { - String code = LintUtils.getBuildCode(api); - if (code != null && code.equalsIgnoreCase(targetApi)) { - return api; - } - } - } - - Node parent = element.getParentNode(); - if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) { - element = (Element) parent; - } else { - break; - } - } - - return -1; - } - - private static void report(final ClassContext context, String message, AbstractInsnNode node, - MethodNode method, String patternStart, String patternEnd, SearchHints hints) { - int lineNumber = node != null ? ClassContext.findLineNumber(node) : -1; - - // If looking for a constructor, the string we'll see in the source is not the - // method name (<init>) but the class name - if (patternStart != null && patternStart.equals(CONSTRUCTOR_NAME) - && node instanceof MethodInsnNode) { - if (hints != null) { - hints = hints.matchConstructor(); - } - patternStart = ((MethodInsnNode) node).owner; - } - - if (patternStart != null) { - int index = patternStart.lastIndexOf('$'); - if (index != -1) { - patternStart = patternStart.substring(index + 1); - } - index = patternStart.lastIndexOf('/'); - if (index != -1) { - patternStart = patternStart.substring(index + 1); - } - } - - Location location = context.getLocationForLine(lineNumber, patternStart, patternEnd, - hints); - context.report(UNSUPPORTED, method, node, location, message, null); - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (mPendingFields != null) { - for (List<Pair<String, Location>> list : mPendingFields.values()) { - for (Pair<String, Location> pair : list) { - String message = pair.getFirst(); - Location location = pair.getSecond(); - context.report(INLINED, location, message, null); - } - } - } - - super.afterCheckProject(context); - } - -// ---- Implements JavaScanner ---- - - @Nullable - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - return new ApiVisitor(context); - } - - @Nullable - @Override - public List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes() { - List<Class<? extends lombok.ast.Node>> types = - new ArrayList<Class<? extends lombok.ast.Node>>(2); - types.add(ImportDeclaration.class); - types.add(Select.class); - types.add(MethodDeclaration.class); - types.add(ConstructorDeclaration.class); - types.add(VariableDefinitionEntry.class); - types.add(VariableReference.class); - return types; - } - - /** - * Checks whether the given instruction is a benign usage of a constant defined in - * a later version of Android than the application's {@code minSdkVersion}. - * - * @param node the instruction to check - * @param name the name of the constant - * @param owner the field owner - * @return true if the given usage is safe on older versions than the introduction - * level of the constant - */ - public boolean isBenignConstantUsage( - @Nullable lombok.ast.Node node, - @NonNull String name, - @NonNull String owner) { - if (owner.equals("android/os/Build$VERSION_CODES")) { //$NON-NLS-1$ - // These constants are required for compilation, not execution - // and valid code checks it even on older platforms - return true; - } - if (owner.equals("android/view/ViewGroup$LayoutParams") //$NON-NLS-1$ - && name.equals("MATCH_PARENT")) { //$NON-NLS-1$ - return true; - } - if (owner.equals("android/widget/AbsListView") //$NON-NLS-1$ - && ((name.equals("CHOICE_MODE_NONE") //$NON-NLS-1$ - || name.equals("CHOICE_MODE_MULTIPLE") //$NON-NLS-1$ - || name.equals("CHOICE_MODE_SINGLE")))) { //$NON-NLS-1$ - // android.widget.ListView#CHOICE_MODE_MULTIPLE and friends have API=1, - // but in API 11 it was moved up to the parent class AbsListView. - // Referencing AbsListView#CHOICE_MODE_MULTIPLE technically requires API 11, - // but the constant is the same as the older version, so accept this without - // warning. - return true; - } - - if (node == null) { - return false; - } - - // It's okay to reference the constant as a case constant (since that - // code path won't be taken) or in a condition of an if statement - lombok.ast.Node curr = node.getParent(); - while (curr != null) { - Class<? extends lombok.ast.Node> nodeType = curr.getClass(); - if (nodeType == Case.class) { - Case caseStatement = (Case) curr; - Expression condition = caseStatement.astCondition(); - return condition != null && isAncestor(condition, node); - } else if (nodeType == If.class) { - If ifStatement = (If) curr; - Expression condition = ifStatement.astCondition(); - return condition != null && isAncestor(condition, node); - } else if (nodeType == InlineIfExpression.class) { - InlineIfExpression ifStatement = (InlineIfExpression) curr; - Expression condition = ifStatement.astCondition(); - return condition != null && isAncestor(condition, node); - } - curr = curr.getParent(); - } - - return false; - } - - private static boolean isAncestor( - @NonNull lombok.ast.Node ancestor, - @Nullable lombok.ast.Node node) { - while (node != null) { - if (node == ancestor) { - return true; - } - node = node.getParent(); - } - - return false; - } - - private final class ApiVisitor extends ForwardingAstVisitor { - private JavaContext mContext; - private Map<String, String> mClassToImport = Maps.newHashMap(); - private List<String> mStarImports; - private Set<String> mLocalVars; - private lombok.ast.Node mCurrentMethod; - private Set<String> mFields; - private List<String> mStaticStarImports; - - private ApiVisitor(JavaContext context) { - mContext = context; - } - - @Override - public boolean visitImportDeclaration(ImportDeclaration node) { - if (node.astStarImport()) { - // Similarly, if you're inheriting from a constants class, figure out - // how that works... :=( - String fqcn = node.asFullyQualifiedName(); - int strip = fqcn.lastIndexOf('*'); - if (strip != -1) { - strip = fqcn.lastIndexOf('.', strip); - if (strip != -1) { - String pkgName = getInternalName(fqcn.substring(0, strip)); - if (ApiLookup.isRelevantOwner(pkgName)) { - if (node.astStaticImport()) { - if (mStaticStarImports == null) { - mStaticStarImports = Lists.newArrayList(); - } - mStaticStarImports.add(pkgName); - } else { - if (mStarImports == null) { - mStarImports = Lists.newArrayList(); - } - mStarImports.add(pkgName); - } - } - } - } - } else if (node.astStaticImport()) { - String fqcn = node.asFullyQualifiedName(); - String fieldName = getInternalName(fqcn); - int index = fieldName.lastIndexOf('$'); - if (index != -1) { - String owner = fieldName.substring(0, index); - String name = fieldName.substring(index + 1); - checkField(node, name, owner); - } - } else { - // Store in map -- if it's "one of ours" - // Use override detector's map for that purpose - String fqcn = node.asFullyQualifiedName(); - - int last = fqcn.lastIndexOf('.'); - if (last != -1) { - String className = fqcn.substring(last + 1); - mClassToImport.put(className, fqcn); - } - } - - return super.visitImportDeclaration(node); - } - - @Override - public boolean visitSelect(Select node) { - boolean result = super.visitSelect(node); - - if (node.getParent() instanceof Select) { - // We only want to look at the leaf expressions; e.g. if you have - // "foo.bar.baz" we only care about the select foo.bar.baz, not foo.bar - return result; - } - - // See if this corresponds to a field reference. We assume it's a field if - // it's a select (x.y) and either the identifier y is capitalized (e.g. - // foo.VIEW_MASK) or if it's a member of an R class (R.id.foo). - String name = node.astIdentifier().astValue(); - boolean isField = Character.isUpperCase(name.charAt(0)); - if (!isField) { - // See if there's an R class - Select current = node; - while (current != null) { - Expression operand = current.astOperand(); - if (operand instanceof Select) { - current = (Select) operand; - if (R_CLASS.equals(current.astIdentifier().astValue())) { - isField = true; - break; - } - } else if (operand instanceof VariableReference) { - VariableReference reference = (VariableReference) operand; - if (R_CLASS.equals(reference.astIdentifier().astValue())) { - isField = true; - } - break; - } else { - break; - } - } - } - - if (isField) { - Expression operand = node.astOperand(); - if (operand.getClass() == Select.class) { - // Possibly a fully qualified name in place - String cls = operand.toString(); - - // See if it's an imported class with an inner class - // (e.g. Manifest.permission.FIELD) - if (Character.isUpperCase(cls.charAt(0))) { - int firstDot = cls.indexOf('.'); - if (firstDot != -1) { - String base = cls.substring(0, firstDot); - String fqcn = mClassToImport.get(base); - if (fqcn != null) { - // Yes imported - String owner = getInternalName(fqcn + cls.substring(firstDot)); - checkField(node, name, owner); - return result; - } - - // Might be a star import: have to iterate and check here - if (mStarImports != null) { - for (String packagePrefix : mStarImports) { - String owner = getInternalName(packagePrefix + '/' + cls); - if (checkField(node, name, owner)) { - mClassToImport.put(name, owner); - return result; - } - } - } - } - } - - // See if it's a fully qualified reference in place - String owner = getInternalName(cls); - checkField(node, name, owner); - return result; - } else if (operand.getClass() == VariableReference.class) { - String className = ((VariableReference) operand).astIdentifier().astValue(); - // Not a FQCN that we care about: look in imports - String fqcn = mClassToImport.get(className); - if (fqcn != null) { - // Yes imported - String owner = getInternalName(fqcn); - checkField(node, name, owner); - return result; - } - - if (Character.isUpperCase(className.charAt(0))) { - // Might be a star import: have to iterate and check here - if (mStarImports != null) { - for (String packagePrefix : mStarImports) { - String owner = getInternalName(packagePrefix) + '/' + className; - if (checkField(node, name, owner)) { - mClassToImport.put(name, owner); - return result; - } - } - } - } - } - } - return result; - } - - @Override - public boolean visitVariableReference(VariableReference node) { - boolean result = super.visitVariableReference(node); - - if (node.getParent() != null) { - lombok.ast.Node parent = node.getParent(); - Class<? extends lombok.ast.Node> parentClass = parent.getClass(); - if (parentClass == Select.class - || parentClass == Switch.class // look up on the switch expression type - || parentClass == Case.class - || parentClass == ConstructorInvocation.class - || parentClass == SuperConstructorInvocation.class - || parentClass == AnnotationElement.class) { - return result; - } - - if (parent instanceof MethodInvocation && - ((MethodInvocation) parent).astOperand() == node) { - return result; - } else if (parent instanceof BinaryExpression) { - BinaryExpression expression = (BinaryExpression) parent; - if (expression.astLeft() == node) { - return result; - } - } - } - - String name = node.astIdentifier().astValue(); - if (Character.isUpperCase(name.charAt(0)) - && (mLocalVars == null || !mLocalVars.contains(name)) - && (mFields == null || !mFields.contains(name))) { - // Potential field reference: check it - if (mStaticStarImports != null) { - for (String owner : mStaticStarImports) { - if (checkField(node, name, owner)) { - break; - } - } - } - } - - return result; - } - - @Override - public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) { - if (mCurrentMethod != null) { - if (mLocalVars == null) { - mLocalVars = Sets.newHashSet(); - } - mLocalVars.add(node.astName().astValue()); - } else { - if (mFields == null) { - mFields = Sets.newHashSet(); - } - mFields.add(node.astName().astValue()); - } - return super.visitVariableDefinitionEntry(node); - } - - @Override - public boolean visitMethodDeclaration(MethodDeclaration node) { - mLocalVars = null; - mCurrentMethod = node; - return super.visitMethodDeclaration(node); - } - - @Override - public boolean visitConstructorDeclaration(ConstructorDeclaration node) { - mLocalVars = null; - mCurrentMethod = node; - return super.visitConstructorDeclaration(node); - } - - @Override - public void endVisit(lombok.ast.Node node) { - if (node == mCurrentMethod) { - mCurrentMethod = null; - } - super.endVisit(node); - } - - /** - * Checks a Java source field reference. Returns true if the field is known - * regardless of whether it's an invalid field or not - */ - private boolean checkField( - @NonNull lombok.ast.Node node, - @NonNull String name, - @NonNull String owner) { - int api = mApiDatabase.getFieldVersion(owner, name); - if (api != -1) { - int minSdk = getMinSdk(mContext); - if (api > minSdk - && api > getLocalMinSdk(node)) { - if (isBenignConstantUsage(node, name, owner)) { - return true; - } - - Location location = mContext.getLocation(node); - String fqcn = getFqcn(owner) + '#' + name; - - if (node instanceof ImportDeclaration) { - // Replace import statement location range with just - // the identifier part - ImportDeclaration d = (ImportDeclaration) node; - int startOffset = d.astParts().first().getPosition().getStart(); - Position start = location.getStart(); - int startColumn = start.getColumn(); - int startLine = start.getLine(); - start = new DefaultPosition(startLine, - startColumn + startOffset - start.getOffset(), startOffset); - int fqcnLength = fqcn.length(); - Position end = new DefaultPosition(startLine, - start.getColumn() + fqcnLength, - start.getOffset() + fqcnLength); - location = Location.create(location.getFile(), start, end); - } - - String message = String.format( - "Field requires API level %1$d (current min is %2$d): %3$s", - api, minSdk, fqcn); - - LintDriver driver = mContext.getDriver(); - if (driver.isSuppressed(INLINED, node)) { - return true; - } - - // Also allow to suppress these issues with NewApi, since some - // fields used to get identified that way - if (driver.isSuppressed(UNSUPPORTED, node)) { - return true; - } - - // We can't report the issue right away; we don't yet know if - // this is an actual inlined (static primitive or String) yet. - // So just make a note of it, and report these after the project - // checking has finished; any fields that aren't inlined will be - // cleared when they're noticed by the class check. - if (mPendingFields == null) { - mPendingFields = Maps.newHashMapWithExpectedSize(20); - } - List<Pair<String, Location>> list = mPendingFields.get(fqcn); - if (list == null) { - list = new ArrayList<Pair<String, Location>>(); - mPendingFields.put(fqcn, list); - } - list.add(Pair.of(message, location)); - } - - return true; - } - - return false; - } - - /** - * Returns the minimum SDK to use according to the given AST node, or null - * if no {@code TargetApi} annotations were found - * - * @return the API level to use for this node, or -1 - */ - public int getLocalMinSdk(@Nullable lombok.ast.Node scope) { - while (scope != null) { - Class<? extends lombok.ast.Node> type = scope.getClass(); - // The Lombok AST uses a flat hierarchy of node type implementation classes - // so no need to do instanceof stuff here. - if (type == VariableDefinition.class) { - // Variable - VariableDefinition declaration = (VariableDefinition) scope; - int targetApi = getLocalMinSdk(declaration.astModifiers()); - if (targetApi != -1) { - return targetApi; - } - } else if (type == MethodDeclaration.class) { - // Method - // Look for annotations on the method - MethodDeclaration declaration = (MethodDeclaration) scope; - int targetApi = getLocalMinSdk(declaration.astModifiers()); - if (targetApi != -1) { - return targetApi; - } - } else if (type == ConstructorDeclaration.class) { - // Constructor - // Look for annotations on the method - ConstructorDeclaration declaration = (ConstructorDeclaration) scope; - int targetApi = getLocalMinSdk(declaration.astModifiers()); - if (targetApi != -1) { - return targetApi; - } - } else if (type == ClassDeclaration.class) { - // Class - ClassDeclaration declaration = (ClassDeclaration) scope; - int targetApi = getLocalMinSdk(declaration.astModifiers()); - if (targetApi != -1) { - return targetApi; - } - } - - scope = scope.getParent(); - } - - return -1; - } - - /** - * Returns true if the given AST modifier has a suppress annotation for the - * given issue (which can be null to check for the "all" annotation) - * - * @param modifiers the modifier to check - * @return true if the issue or all issues should be suppressed for this - * modifier - */ - private int getLocalMinSdk(@Nullable Modifiers modifiers) { - if (modifiers == null) { - return -1; - } - StrictListAccessor<Annotation, Modifiers> annotations = modifiers.astAnnotations(); - if (annotations == null) { - return -1; - } - - Iterator<Annotation> iterator = annotations.iterator(); - while (iterator.hasNext()) { - Annotation annotation = iterator.next(); - TypeReference t = annotation.astAnnotationTypeReference(); - String typeName = t.getTypeName(); - if (typeName.endsWith(TARGET_API)) { - StrictListAccessor<AnnotationElement, Annotation> values = - annotation.astElements(); - if (values != null) { - Iterator<AnnotationElement> valueIterator = values.iterator(); - while (valueIterator.hasNext()) { - AnnotationElement element = valueIterator.next(); - AnnotationValue valueNode = element.astValue(); - if (valueNode == null) { - continue; - } - if (valueNode instanceof IntegralLiteral) { - IntegralLiteral literal = (IntegralLiteral) valueNode; - return literal.astIntValue(); - } else if (valueNode instanceof StringLiteral) { - String value = ((StringLiteral) valueNode).astValue(); - return codeNameToApi(value); - } else if (valueNode instanceof Select) { - Select select = (Select) valueNode; - String codename = select.astIdentifier().astValue(); - return codeNameToApi(codename); - } else if (valueNode instanceof VariableReference) { - VariableReference reference = (VariableReference) valueNode; - String codename = reference.astIdentifier().astValue(); - return codeNameToApi(codename); - } - } - } - } - } - - return -1; - } - } - - private static int codeNameToApi(String codename) { - for (int api = 1; api <= SdkConstants.HIGHEST_KNOWN_API; api++) { - String code = LintUtils.getBuildCode(api); - if (code != null && code.equalsIgnoreCase(codename)) { - return api; - } - } - - return -1; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiLookup.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiLookup.java deleted file mode 100644 index 6a4de6e..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiLookup.java +++ /dev/null @@ -1,971 +0,0 @@ -/* - * 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.ANDROID_PKG; -import static com.android.SdkConstants.DOT_XML; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.annotations.VisibleForTesting; -import com.android.tools.lint.client.api.LintClient; -import com.android.tools.lint.detector.api.LintUtils; -import com.google.common.base.Charsets; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.io.Files; -import com.google.common.primitives.UnsignedBytes; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel.MapMode; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Database for API checking: Allows quick lookup of a given class, method or field - * to see which API level it was introduced in. - * <p> - * This class is optimized for quick bytecode lookup used in conjunction with the - * ASM library: It has lookup methods that take internal JVM signatures, and for a method - * call for example it processes the owner, name and description parameters separately - * the way they are provided from ASM. - * <p> - * The {@link Api} class provides access to the full Android API along with version - * information, initialized from an XML file. This lookup class adds a binary cache around - * the API to make initialization faster and to require fewer objects. It creates - * a binary cache data structure, which fits in a single byte array, which means that - * to open the database you can just read in the byte array and go. On one particular - * machine, this takes about 30-50 ms versus 600-800ms for the full parse. It also - * helps memory by placing everything in a compact byte array instead of needing separate - * strings (2 bytes per character in a char[] for the 25k method entries, 11k field entries - * and 6k class entries) - and it also avoids the same number of Map.Entry objects. - * When creating the memory data structure it performs a few other steps to help memory: - * <ul> - * <li> It stores the strings as single bytes, since all the JVM signatures are in ASCII - * <li> It strips out the method return types (which takes the binary size down from - * about 4.7M to 4.0M) - * <li> It strips out all APIs that have since=1, since the lookup only needs to find - * classes, methods and fields that have an API level *higher* than 1. This drops - * the memory use down from 4.0M to 1.7M. - * </ul> - */ -public class ApiLookup { - /** Relative path to the api-versions.xml database file within the Lint installation */ - private static final String XML_FILE_PATH = "platform-tools/api/api-versions.xml"; //$NON-NLS-1$ - private static final String FILE_HEADER = "API database used by Android lint\000"; - private static final int BINARY_FORMAT_VERSION = 5; - private static final boolean DEBUG_FORCE_REGENERATE_BINARY = false; - private static final boolean DEBUG_SEARCH = false; - private static final boolean WRITE_STATS = false; - /** Default size to reserve for each API entry when creating byte buffer to build up data */ - private static final int BYTES_PER_ENTRY = 36; - - private final LintClient mClient; - private final File mXmlFile; - private final File mBinaryFile; - private final Api mInfo; - private byte[] mData; - private int[] mIndices; - private int mClassCount; - private String[] mJavaPackages; - - private static WeakReference<ApiLookup> sInstance = - new WeakReference<ApiLookup>(null); - - /** - * Returns an instance of the API database - * - * @param client the client to associate with this database - used only for - * logging. The database object may be shared among repeated invocations, - * and in that case client used will be the one originally passed in. - * In other words, this parameter may be ignored if the client created - * is not new. - * @return a (possibly shared) instance of the API database, or null - * if its data can't be found - */ - public static ApiLookup get(LintClient client) { - synchronized (ApiLookup.class) { - ApiLookup db = sInstance.get(); - if (db == null) { - File file = client.findResource(XML_FILE_PATH); - if (file == null) { - // AOSP build environment? - String build = System.getenv("ANDROID_BUILD_TOP"); //$NON-NLS-1$ - if (build != null) { - file = new File(build, "development/sdk/api-versions.xml" //$NON-NLS-1$ - .replace('/', File.separatorChar)); - } - } - - if (file == null || !file.exists()) { - client.log(null, "Fatal error: No API database found at %1$s", file); - return null; - } else { - db = get(client, file); - } - sInstance = new WeakReference<ApiLookup>(db); - } - - return db; - } - } - - @VisibleForTesting - static String getCacheFileName(String xmlFileName) { - if (LintUtils.endsWith(xmlFileName, DOT_XML)) { - xmlFileName = xmlFileName.substring(0, xmlFileName.length() - DOT_XML.length()); - } - - // Incorporate version number in the filename to avoid upgrade filename - // conflicts on Windows (such as issue #26663) - return xmlFileName + '-' + BINARY_FORMAT_VERSION + ".bin"; //$NON-NLS-1$ - } - - /** - * Returns an instance of the API database - * - * @param client the client to associate with this database - used only for - * logging - * @param xmlFile the XML file containing configuration data to use for this - * database - * @return a (possibly shared) instance of the API database, or null - * if its data can't be found - */ - private static ApiLookup get(LintClient client, File xmlFile) { - if (!xmlFile.exists()) { - client.log(null, "The API database file %1$s does not exist", xmlFile); - return null; - } - - File cacheDir = client.getCacheDir(true/*create*/); - if (cacheDir == null) { - cacheDir = xmlFile.getParentFile(); - } - - File binaryData = new File(cacheDir, getCacheFileName(xmlFile.getName())); - - if (DEBUG_FORCE_REGENERATE_BINARY) { - System.err.println("\nTemporarily regenerating binary data unconditionally \nfrom " - + xmlFile + "\nto " + binaryData); - if (!createCache(client, xmlFile, binaryData)) { - return null; - } - } else if (!binaryData.exists() || binaryData.lastModified() < xmlFile.lastModified() - || binaryData.length() == 0) { - if (!createCache(client, xmlFile, binaryData)) { - return null; - } - } - - if (!binaryData.exists()) { - client.log(null, "The API database file %1$s does not exist", binaryData); - return null; - } - - return new ApiLookup(client, xmlFile, binaryData, null); - } - - private static boolean createCache(LintClient client, File xmlFile, File binaryData) { - long begin = 0; - if (WRITE_STATS) { - begin = System.currentTimeMillis(); - } - - Api info = Api.parseApi(xmlFile); - - if (WRITE_STATS) { - long end = System.currentTimeMillis(); - System.out.println("Reading XML data structures took " + (end - begin) + " ms)"); - } - - if (info != null) { - try { - writeDatabase(binaryData, info); - return true; - } catch (IOException ioe) { - client.log(ioe, "Can't write API cache file"); - } - } - - return false; - } - - /** Use one of the {@link #get} factory methods instead */ - private ApiLookup( - @NonNull LintClient client, - @NonNull File xmlFile, - @Nullable File binaryFile, - @Nullable Api info) { - mClient = client; - mXmlFile = xmlFile; - mBinaryFile = binaryFile; - mInfo = info; - - if (binaryFile != null) { - readData(); - } - } - - /** - * Database format: - * <pre> - * 1. A file header, which is the exact contents of {@link #FILE_HEADER} encoded - * as ASCII characters. The purpose of the header is to identify what the file - * is for, for anyone attempting to open the file. - * 2. A file version number. If the binary file does not match the reader's expected - * version, it can ignore it (and regenerate the cache from XML). - * 3. The number of classes [1 int] - * 4. The number of members (across all classes) [1 int]. - * 5. The number of java/javax packages [1 int] - * 6. The java/javax package name table. Each item consists of a byte count for - * the package string (as 1 byte) followed by the UTF-8 encoded bytes for each package. - * These are in sorted order. - * 7. Class offset table (one integer per class, pointing to the byte offset in the - * file (relative to the beginning of the file) where each class begins. - * The classes are always sorted alphabetically by fully qualified name. - * 8. Member offset table (one integer per member, pointing to the byte offset in the - * file (relative to the beginning of the file) where each member entry begins. - * The members are always sorted alphabetically. - * 9. Class entry table. Each class entry consists of the fully qualified class name, - * in JVM format (using / instead of . in package names and $ for inner classes), - * followed by the byte 0 as a terminator, followed by the API version as a byte. - * 10. Member entry table. Each member entry consists of the class number (as a short), - * followed by the JVM method/field signature, encoded as UTF-8, followed by a 0 byte - * signature terminator, followed by the API level as a byte. - * <p> - * TODO: Pack the offsets: They increase by a small amount for each entry, so no need - * to spend 4 bytes on each. These will need to be processed when read back in anyway, - * so consider storing the offset -deltas- as single bytes and adding them up cumulatively - * in readData(). - * </pre> - */ - private void readData() { - if (!mBinaryFile.exists()) { - mClient.log(null, "%1$s does not exist", mBinaryFile); - return; - } - long start = System.currentTimeMillis(); - try { - MappedByteBuffer buffer = Files.map(mBinaryFile, MapMode.READ_ONLY); - assert buffer.order() == ByteOrder.BIG_ENDIAN; - - // First skip the header - byte[] expectedHeader = FILE_HEADER.getBytes(Charsets.US_ASCII); - buffer.rewind(); - for (int offset = 0; offset < expectedHeader.length; offset++) { - if (expectedHeader[offset] != buffer.get()) { - mClient.log(null, "Incorrect file header: not an API database cache " + - "file, or a corrupt cache file"); - return; - } - } - - // Read in the format number - if (buffer.get() != BINARY_FORMAT_VERSION) { - // Force regeneration of new binary data with up to date format - if (createCache(mClient, mXmlFile, mBinaryFile)) { - readData(); // Recurse - } - - return; - } - - mClassCount = buffer.getInt(); - int methodCount = buffer.getInt(); - - int javaPackageCount = buffer.getInt(); - // Read in the Java packages - mJavaPackages = new String[javaPackageCount]; - for (int i = 0; i < javaPackageCount; i++) { - int count = UnsignedBytes.toInt(buffer.get()); - byte[] bytes = new byte[count]; - buffer.get(bytes, 0, count); - mJavaPackages[i] = new String(bytes, Charsets.UTF_8); - } - - // Read in the class table indices; - int count = mClassCount + methodCount; - int[] offsets = new int[count]; - - // Another idea: I can just store the DELTAS in the file (and add them up - // when reading back in) such that it takes just ONE byte instead of four! - - for (int i = 0; i < count; i++) { - offsets[i] = buffer.getInt(); - } - - // No need to read in the rest -- we'll just keep the whole byte array in memory - // TODO: Make this code smarter/more efficient. - int size = buffer.limit(); - byte[] b = new byte[size]; - buffer.rewind(); - buffer.get(b); - mData = b; - mIndices = offsets; - - // TODO: We only need to keep the data portion here since we've initialized - // the offset array separately. - // TODO: Investigate (profile) accessing the byte buffer directly instead of - // accessing a byte array. - } catch (Throwable e) { - mClient.log(null, "Failure reading binary cache file %1$s", mBinaryFile.getPath()); - mClient.log(null, "Please delete the file and restart the IDE/lint: %1$s", - mBinaryFile.getPath()); - mClient.log(e, null); - } - if (WRITE_STATS) { - long end = System.currentTimeMillis(); - System.out.println("\nRead API database in " + (end - start) - + " milliseconds."); - System.out.println("Size of data table: " + mData.length + " bytes (" - + Integer.toString(mData.length / 1024) + "k)\n"); - } - } - - /** See the {@link #readData()} for documentation on the data format. */ - private static void writeDatabase(File file, Api info) throws IOException { - /* - * 1. A file header, which is the exact contents of {@link FILE_HEADER} encoded - * as ASCII characters. The purpose of the header is to identify what the file - * is for, for anyone attempting to open the file. - * 2. A file version number. If the binary file does not match the reader's expected - * version, it can ignore it (and regenerate the cache from XML). - */ - Map<String, ApiClass> classMap = info.getClasses(); - // Write the class table - - List<String> classes = new ArrayList<String>(classMap.size()); - Map<ApiClass, List<String>> memberMap = - Maps.newHashMapWithExpectedSize(classMap.size()); - int memberCount = 0; - Set<String> javaPackageSet = Sets.newHashSetWithExpectedSize(70); - for (Map.Entry<String, ApiClass> entry : classMap.entrySet()) { - String className = entry.getKey(); - ApiClass apiClass = entry.getValue(); - - if (className.startsWith("java/") //$NON-NLS-1$ - || className.startsWith("javax/")) { //$NON-NLS-1$ - String pkg = apiClass.getPackage(); - javaPackageSet.add(pkg); - } - - if (!isRelevantOwner(className)) { - System.out.println("Warning: The isRelevantOwner method does not pass " - + className); - } - - Set<String> allMethods = apiClass.getAllMethods(info); - Set<String> allFields = apiClass.getAllFields(info); - - // Strip out all members that have been supported since version 1. - // This makes the database *much* leaner (down from about 4M to about - // 1.7M), and this just fills the table with entries that ultimately - // don't help the API checker since it just needs to know if something - // requires a version *higher* than the minimum. If in the future the - // database needs to answer queries about whether a method is public - // or not, then we'd need to put this data back in. - List<String> members = new ArrayList<String>(allMethods.size() + allFields.size()); - for (String member : allMethods) { - - Integer since = apiClass.getMethod(member, info); - if (since == null) { - assert false : className + ':' + member; - since = 1; - } - if (since != 1) { - members.add(member); - } - } - - // Strip out all members that have been supported since version 1. - // This makes the database *much* leaner (down from about 4M to about - // 1.7M), and this just fills the table with entries that ultimately - // don't help the API checker since it just needs to know if something - // requires a version *higher* than the minimum. If in the future the - // database needs to answer queries about whether a method is public - // or not, then we'd need to put this data back in. - for (String member : allFields) { - Integer since = apiClass.getField(member, info); - if (since == null) { - assert false : className + ':' + member; - since = 1; - } - if (since != 1) { - members.add(member); - } - } - - // Only include classes that have one or more members requiring version 2 or higher: - if (!members.isEmpty()) { - classes.add(className); - memberMap.put(apiClass, members); - memberCount += members.size(); - } - } - Collections.sort(classes); - - List<String> javaPackages = Lists.newArrayList(javaPackageSet); - Collections.sort(javaPackages); - int javaPackageCount = javaPackages.size(); - - int entryCount = classMap.size() + memberCount; - int capacity = entryCount * BYTES_PER_ENTRY; - ByteBuffer buffer = ByteBuffer.allocate(capacity); - buffer.order(ByteOrder.BIG_ENDIAN); - // 1. A file header, which is the exact contents of {@link FILE_HEADER} encoded - // as ASCII characters. The purpose of the header is to identify what the file - // is for, for anyone attempting to open the file. - - buffer.put(FILE_HEADER.getBytes(Charsets.US_ASCII)); - - // 2. A file version number. If the binary file does not match the reader's expected - // version, it can ignore it (and regenerate the cache from XML). - buffer.put((byte) BINARY_FORMAT_VERSION); - - // 3. The number of classes [1 int] - buffer.putInt(classes.size()); - - // 4. The number of members (across all classes) [1 int]. - buffer.putInt(memberCount); - - // 5. The number of Java packages [1 int]. - buffer.putInt(javaPackageCount); - - // 6. The Java package table. There are javaPackage.size() entries, where each entry - // consists of a string length, as a byte, followed by the bytes in the package. - // There is no terminating 0. - for (String pkg : javaPackages) { - byte[] bytes = pkg.getBytes(Charsets.UTF_8); - assert bytes.length < 255 : pkg; - buffer.put((byte) bytes.length); - buffer.put(bytes); - } - - // 7. Class offset table (one integer per class, pointing to the byte offset in the - // file (relative to the beginning of the file) where each class begins. - // The classes are always sorted alphabetically by fully qualified name. - int classOffsetTable = buffer.position(); - - // Reserve enough room for the offset table here: we will backfill it with pointers - // as we're writing out the data structures below - for (int i = 0, n = classes.size(); i < n; i++) { - buffer.putInt(0); - } - - // 8. Member offset table (one integer per member, pointing to the byte offset in the - // file (relative to the beginning of the file) where each member entry begins. - // The members are always sorted alphabetically. - int methodOffsetTable = buffer.position(); - for (int i = 0, n = memberCount; i < n; i++) { - buffer.putInt(0); - } - - int nextEntry = buffer.position(); - int nextOffset = classOffsetTable; - - // 9. Class entry table. Each class entry consists of the fully qualified class name, - // in JVM format (using / instead of . in package names and $ for inner classes), - // followed by the byte 0 as a terminator, followed by the API version as a byte. - for (String clz : classes) { - buffer.position(nextOffset); - buffer.putInt(nextEntry); - nextOffset = buffer.position(); - buffer.position(nextEntry); - buffer.put(clz.getBytes(Charsets.UTF_8)); - buffer.put((byte) 0); - - ApiClass apiClass = classMap.get(clz); - assert apiClass != null : clz; - int since = apiClass.getSince(); - assert since == UnsignedBytes.toInt((byte) since) : since; // make sure it fits - buffer.put((byte) since); - - nextEntry = buffer.position(); - } - - // 10. Member entry table. Each member entry consists of the class number (as a short), - // followed by the JVM method/field signature, encoded as UTF-8, followed by a 0 byte - // signature terminator, followed by the API level as a byte. - assert nextOffset == methodOffsetTable; - - for (int classNumber = 0, n = classes.size(); classNumber < n; classNumber++) { - String clz = classes.get(classNumber); - ApiClass apiClass = classMap.get(clz); - assert apiClass != null : clz; - List<String> members = memberMap.get(apiClass); - Collections.sort(members); - - for (String member : members) { - buffer.position(nextOffset); - buffer.putInt(nextEntry); - nextOffset = buffer.position(); - buffer.position(nextEntry); - - Integer since; - if (member.indexOf('(') != -1) { - since = apiClass.getMethod(member, info); - } else { - since = apiClass.getField(member, info); - } - if (since == null) { - assert false : clz + ':' + member; - since = 1; - } - - assert classNumber == (short) classNumber; - buffer.putShort((short) classNumber); - byte[] signature = member.getBytes(Charsets.UTF_8); - for (int i = 0; i < signature.length; i++) { - // Make sure all signatures are really just simple ASCII - byte b = signature[i]; - assert b == (b & 0x7f) : member; - buffer.put(b); - // Skip types on methods - if (b == (byte) ')') { - break; - } - } - buffer.put((byte) 0); - int api = since; - assert api == UnsignedBytes.toInt((byte) api); - //assert api >= 1 && api < 0xFF; // max that fits in a byte - buffer.put((byte) api); - nextEntry = buffer.position(); - } - } - - int size = buffer.position(); - assert size <= buffer.limit(); - buffer.mark(); - - if (WRITE_STATS) { - System.out.println("Wrote " + classes.size() + " classes and " - + memberCount + " member entries"); - System.out.print("Actual binary size: " + size + " bytes"); - System.out.println(String.format(" (%.1fM)", size/(1024*1024.f))); - - System.out.println("Allocated size: " + (entryCount * BYTES_PER_ENTRY) + " bytes"); - System.out.println("Required bytes per entry: " + (size/ entryCount) + " bytes"); - } - - // Now dump this out as a file - // There's probably an API to do this more efficiently; TODO: Look into this. - byte[] b = new byte[size]; - buffer.rewind(); - buffer.get(b); - if (file.exists()) { - file.delete(); - } - FileOutputStream output = Files.newOutputStreamSupplier(file).getOutput(); - output.write(b); - output.close(); - } - - // For debugging only - private String dumpEntry(int offset) { - if (DEBUG_SEARCH) { - StringBuilder sb = new StringBuilder(200); - for (int i = offset; i < mData.length; i++) { - if (mData[i] == 0) { - break; - } - char c = (char) UnsignedBytes.toInt(mData[i]); - sb.append(c); - } - - return sb.toString(); - } else { - return "<disabled>"; //$NON-NLS-1$ - } - } - - private static int compare(byte[] data, int offset, byte terminator, String s, int max) { - int i = offset; - int j = 0; - for (; j < max; i++, j++) { - byte b = data[i]; - char c = s.charAt(j); - // TODO: Check somewhere that the strings are purely in the ASCII range; if not - // they're not a match in the database - byte cb = (byte) c; - int delta = b - cb; - if (delta != 0) { - return delta; - } - } - - return data[i] - terminator; - } - - /** - * Quick determination whether a given class name is possibly interesting; this - * is a quick package prefix check to determine whether we need to consider - * the class at all. This let's us do less actual searching for the vast majority - * of APIs (in libraries, application code etc) that have nothing to do with the - * APIs in our packages. - * @param name the class name in VM format (e.g. using / instead of .) - * @return true if the owner is <b>possibly</b> relevant - */ - public boolean isRelevantClass(String name) { - // TODO: Add quick switching here. This is tied to the database file so if - // we end up with unexpected prefixes there, this could break. For that reason, - // for now we consider everything relevant. - return true; - } - - /** - * Returns the API version required by the given class reference, - * or -1 if this is not a known API class. Note that it may return -1 - * for classes introduced in version 1; internally the database only - * stores version data for version 2 and up. - * - * @param className the internal name of the class, e.g. its - * fully qualified name (as returned by Class.getName(), but with - * '.' replaced by '/'. - * @return the minimum API version the method is supported for, or -1 if - * it's unknown <b>or version 1</b>. - */ - public int getClassVersion(@NonNull String className) { - if (!isRelevantClass(className)) { - return -1; - } - - if (mData != null) { - int classNumber = findClass(className); - if (classNumber != -1) { - int offset = mIndices[classNumber]; - while (mData[offset] != 0) { - offset++; - } - offset++; - return UnsignedBytes.toInt(mData[offset]); - } - } else { - ApiClass clz = mInfo.getClass(className); - if (clz != null) { - int since = clz.getSince(); - if (since == Integer.MAX_VALUE) { - since = -1; - } - return since; - } - } - - return -1; - } - - /** - * Returns the API version required by the given method call. The method is - * referred to by its {@code owner}, {@code name} and {@code desc} fields. - * If the method is unknown it returns -1. Note that it may return -1 for - * classes introduced in version 1; internally the database only stores - * version data for version 2 and up. - * - * @param owner the internal name of the method's owner class, e.g. its - * fully qualified name (as returned by Class.getName(), but with - * '.' replaced by '/'. - * @param name the method's name - * @param desc the method's descriptor - see {@link org.objectweb.asm.Type} - * @return the minimum API version the method is supported for, or -1 if - * it's unknown <b>or version 1</b>. - */ - public int getCallVersion( - @NonNull String owner, - @NonNull String name, - @NonNull String desc) { - if (!isRelevantClass(owner)) { - return -1; - } - - if (mData != null) { - int classNumber = findClass(owner); - if (classNumber != -1) { - return findMember(classNumber, name, desc); - } - } else { - ApiClass clz = mInfo.getClass(owner); - if (clz != null) { - String signature = name + desc; - int since = clz.getMethod(signature, mInfo); - if (since == Integer.MAX_VALUE) { - since = -1; - } - return since; - } - } - - return -1; - } - - /** - * Returns the API version required to access the given field, or -1 if this - * is not a known API method. Note that it may return -1 for classes - * introduced in version 1; internally the database only stores version data - * for version 2 and up. - * - * @param owner the internal name of the method's owner class, e.g. its - * fully qualified name (as returned by Class.getName(), but with - * '.' replaced by '/'. - * @param name the method's name - * @return the minimum API version the method is supported for, or -1 if - * it's unknown <b>or version 1</b> - */ - public int getFieldVersion( - @NonNull String owner, - @NonNull String name) { - if (!isRelevantClass(owner)) { - return -1; - } - - if (mData != null) { - int classNumber = findClass(owner); - if (classNumber != -1) { - return findMember(classNumber, name, null); - } - } else { - ApiClass clz = mInfo.getClass(owner); - if (clz != null) { - int since = clz.getField(name, mInfo); - if (since == Integer.MAX_VALUE) { - since = -1; - } - return since; - } - } - - return -1; - } - - /** - * Returns true if the given owner (in VM format) is relevant to the database. - * This allows quick filtering out of owners that won't return any data - * for the various {@code #getFieldVersion} etc methods. - * - * @param owner the owner to look up - * @return true if the owner might be relevant to the API database - */ - public static boolean isRelevantOwner(@NonNull String owner) { - if (owner.startsWith("java")) { //$NON-NLS-1$ // includes javax/ - return true; - } - if (owner.startsWith(ANDROID_PKG)) { - if (owner.startsWith("/support/", 7)) { //$NON-NLS-1$ - return false; - } - return true; - } else if (owner.startsWith("org/")) { //$NON-NLS-1$ - if (owner.startsWith("xml", 4) //$NON-NLS-1$ - || owner.startsWith("w3c/", 4) //$NON-NLS-1$ - || owner.startsWith("json/", 4) //$NON-NLS-1$ - || owner.startsWith("apache/", 4)) { //$NON-NLS-1$ - return true; - } - } else if (owner.startsWith("com/")) { //$NON-NLS-1$ - if (owner.startsWith("google/", 4) //$NON-NLS-1$ - || owner.startsWith("android/", 4)) { //$NON-NLS-1$ - return true; - } - } else if (owner.startsWith("junit") //$NON-NLS-1$ - || owner.startsWith("dalvik")) { //$NON-NLS-1$ - return true; - } - - return false; - } - - - /** - * Returns true if the given owner (in VM format) is a valid Java package supported - * in any version of Android. - * - * @param owner the package, in VM format - * @return true if the package is included in one or more versions of Android - */ - public boolean isValidJavaPackage(@NonNull String owner) { - int packageLength = owner.lastIndexOf('/'); - if (packageLength == -1) { - return false; - } - - // The index array contains class indexes from 0 to classCount and - // member indices from classCount to mIndices.length. - int low = 0; - int high = mJavaPackages.length - 1; - while (low <= high) { - int middle = (low + high) >>> 1; - int offset = middle; - - if (DEBUG_SEARCH) { - System.out.println("Comparing string " + owner + " with entry at " + offset - + ": " + mJavaPackages[offset]); - } - - // Compare the api info at the given index. - int compare = comparePackage(mJavaPackages[offset], owner, packageLength); - if (compare == 0) { - return true; - } - - if (compare < 0) { - low = middle + 1; - } else if (compare > 0) { - high = middle - 1; - } else { - assert false; // compare == 0 already handled above - return false; - } - } - - return false; - } - - private static int comparePackage(String s1, String s2, int max) { - for (int i = 0; i < max; i++) { - if (i == s1.length()) { - return -1; - } - char c1 = s1.charAt(i); - char c2 = s2.charAt(i); - if (c1 != c2) { - return c1 - c2; - } - } - - if (s1.length() > max) { - return 1; - } - - return 0; - } - - /** Returns the class number of the given class, or -1 if it is unknown */ - private int findClass(@NonNull String owner) { - assert owner.indexOf('.') == -1 : "Should use / instead of . in owner: " + owner; - - // The index array contains class indexes from 0 to classCount and - // member indices from classCount to mIndices.length. - int low = 0; - int high = mClassCount - 1; - // Compare the api info at the given index. - int classNameLength = owner.length(); - while (low <= high) { - int middle = (low + high) >>> 1; - int offset = mIndices[middle]; - - if (DEBUG_SEARCH) { - System.out.println("Comparing string " + owner + " with entry at " + offset - + ": " + dumpEntry(offset)); - } - - int compare = compare(mData, offset, (byte) 0, owner, classNameLength); - if (compare == 0) { - return middle; - } - - if (compare < 0) { - low = middle + 1; - } else if (compare > 0) { - high = middle - 1; - } else { - assert false; // compare == 0 already handled above - return -1; - } - } - - return -1; - } - - private int findMember(int classNumber, @NonNull String name, @Nullable String desc) { - // The index array contains class indexes from 0 to classCount and - // member indices from classCount to mIndices.length. - int low = mClassCount; - int high = mIndices.length - 1; - while (low <= high) { - int middle = (low + high) >>> 1; - int offset = mIndices[middle]; - - if (DEBUG_SEARCH) { - System.out.println("Comparing string " + (name + ';' + desc) + - " with entry at " + offset + ": " + dumpEntry(offset)); - } - - // Check class number: read short. The byte data is always big endian. - int entryClass = (mData[offset++] & 0xFF) << 8 | (mData[offset++] & 0xFF); - int compare = entryClass - classNumber; - if (compare == 0) { - if (desc != null) { - // Method - int nameLength = name.length(); - compare = compare(mData, offset, (byte) '(', name, nameLength); - if (compare == 0) { - offset += nameLength; - int argsEnd = desc.indexOf(')'); - // Only compare up to the ) -- after that we have a return value in the - // input description, which isn't there in the database - compare = compare(mData, offset, (byte) ')', desc, argsEnd); - if (compare == 0) { - offset += argsEnd + 1; - - if (mData[offset++] == 0) { - // Yes, terminated argument list: get the API level - return UnsignedBytes.toInt(mData[offset]); - } - } - } - } else { - // Field - int nameLength = name.length(); - compare = compare(mData, offset, (byte) 0, name, nameLength); - if (compare == 0) { - offset += nameLength; - if (mData[offset++] == 0) { - // Yes, terminated argument list: get the API level - return UnsignedBytes.toInt(mData[offset]); - } - } - } - } - - if (compare < 0) { - low = middle + 1; - } else if (compare > 0) { - high = middle - 1; - } else { - assert false; // compare == 0 already handled above - return -1; - } - } - - return -1; - } - - /** Clears out any existing lookup instances */ - @VisibleForTesting - static void dispose() { - sInstance.clear(); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiParser.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiParser.java deleted file mode 100644 index b3c2f2a..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ApiParser.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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 org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import java.util.HashMap; -import java.util.Map; - -/** - * Parser for the simplified XML API format version 1. - */ -public class ApiParser extends DefaultHandler { - - private static final String NODE_API = "api"; - private static final String NODE_CLASS = "class"; - private static final String NODE_FIELD = "field"; - private static final String NODE_METHOD = "method"; - private static final String NODE_EXTENDS = "extends"; - private static final String NODE_IMPLEMENTS = "implements"; - - private static final String ATTR_NAME = "name"; - private static final String ATTR_SINCE = "since"; - - private final Map<String, ApiClass> mClasses = new HashMap<String, ApiClass>(); - - private ApiClass mCurrentClass; - - ApiParser() { - } - - Map<String, ApiClass> getClasses() { - return mClasses; - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) - throws SAXException { - - if (localName == null || localName.isEmpty()) { - localName = qName; - } - - try { - if (NODE_API.equals(localName)) { - // do nothing. - - } else if (NODE_CLASS.equals(localName)) { - String name = attributes.getValue(ATTR_NAME); - int since = Integer.parseInt(attributes.getValue(ATTR_SINCE)); - - mCurrentClass = addClass(name, since); - - } else if (NODE_EXTENDS.equals(localName)) { - String name = attributes.getValue(ATTR_NAME); - int since = getSince(attributes); - - mCurrentClass.addSuperClass(name, since); - - } else if (NODE_IMPLEMENTS.equals(localName)) { - String name = attributes.getValue(ATTR_NAME); - int since = getSince(attributes); - - mCurrentClass.addInterface(name, since); - - } else if (NODE_METHOD.equals(localName)) { - String name = attributes.getValue(ATTR_NAME); - int since = getSince(attributes); - - mCurrentClass.addMethod(name, since); - - } else if (NODE_FIELD.equals(localName)) { - String name = attributes.getValue(ATTR_NAME); - int since = getSince(attributes); - - mCurrentClass.addField(name, since); - - } - - } finally { - super.startElement(uri, localName, qName, attributes); - } - } - - private ApiClass addClass(String name, int apiLevel) { - ApiClass theClass = mClasses.get(name); - if (theClass == null) { - theClass = new ApiClass(name, apiLevel); - mClasses.put(name, theClass); - } - - return theClass; - } - - private int getSince(Attributes attributes) { - int since = mCurrentClass.getSince(); - String sinceAttr = attributes.getValue(ATTR_SINCE); - - if (sinceAttr != null) { - since = Integer.parseInt(sinceAttr); - } - - return since; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ArraySizeDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ArraySizeDetector.java deleted file mode 100644 index 8bbcffb..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ArraySizeDetector.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.TAG_ARRAY; -import static com.android.SdkConstants.TAG_INTEGER_ARRAY; -import static com.android.SdkConstants.TAG_STRING_ARRAY; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.client.api.LintDriver; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 com.android.utils.Pair; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Checks for arrays with inconsistent item counts - */ -public class ArraySizeDetector extends ResourceXmlDetector { - - /** Are there differences in how many array elements are declared? */ - public static final Issue INCONSISTENT = Issue.create( - "InconsistentArrays", //$NON-NLS-1$ - "Checks for inconsistencies in the number of elements in arrays", - "When an array is translated in a different locale, it should normally have " + - "the same number of elements as the original array. When adding or removing " + - "elements to an array, it is easy to forget to update all the locales, and this " + - "lint warning finds inconsistencies like these.\n" + - "\n" + - "Note however that there may be cases where you really want to declare a " + - "different number of array items in each configuration (for example where " + - "the array represents available options, and those options differ for " + - "different layout orientations and so on), so use your own judgement to " + - "decide if this is really an error.\n" + - "\n" + - "You can suppress this error type if it finds false errors in your project.", - Category.CORRECTNESS, - 7, - Severity.WARNING, - ArraySizeDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - private Multimap<File, Pair<String, Integer>> mFileToArrayCount; - - /** Locations for each array name. Populated during phase 2, if necessary */ - private Map<String, Location> mLocations; - - /** Error messages for each array name. Populated during phase 2, if necessary */ - private Map<String, String> mDescriptions; - - /** Constructs a new {@link ArraySizeDetector} */ - public ArraySizeDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.VALUES; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - TAG_ARRAY, - TAG_STRING_ARRAY, - TAG_INTEGER_ARRAY - ); - } - - @Override - public void beforeCheckProject(@NonNull Context context) { - if (context.getPhase() == 1) { - mFileToArrayCount = ArrayListMultimap.create(30, 5); - } - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (context.getPhase() == 1) { - // Check that all arrays for the same name have the same number of translations - - Set<String> alreadyReported = new HashSet<String>(); - Map<String, Integer> countMap = new HashMap<String, Integer>(); - Map<String, File> fileMap = new HashMap<String, File>(); - - // Process the file in sorted file order to ensure stable output - List<File> keys = new ArrayList<File>(mFileToArrayCount.keySet()); - Collections.sort(keys); - - for (File file : keys) { - Collection<Pair<String, Integer>> pairs = mFileToArrayCount.get(file); - for (Pair<String, Integer> pair : pairs) { - String name = pair.getFirst(); - - if (alreadyReported.contains(name)) { - continue; - } - Integer count = pair.getSecond(); - - Integer current = countMap.get(name); - if (current == null) { - countMap.put(name, count); - fileMap.put(name, file); - } else if (!count.equals(current)) { - alreadyReported.add(name); - - if (mLocations == null) { - mLocations = new HashMap<String, Location>(); - mDescriptions = new HashMap<String, String>(); - } - mLocations.put(name, null); - - String thisName = file.getParentFile().getName() + File.separator - + file.getName(); - File otherFile = fileMap.get(name); - String otherName = otherFile.getParentFile().getName() + File.separator - + otherFile.getName(); - String message = String.format( - "Array %1$s has an inconsistent number of items (%2$d in %3$s, %4$d in %5$s)", - name, count, thisName, current, otherName); - mDescriptions.put(name, message); - } - } - } - - if (mLocations != null) { - // Request another scan through the resources such that we can - // gather the actual locations - context.getDriver().requestRepeat(this, Scope.ALL_RESOURCES_SCOPE); - } - mFileToArrayCount = null; - } else { - if (mLocations != null) { - List<String> names = new ArrayList<String>(mLocations.keySet()); - Collections.sort(names); - for (String name : names) { - Location location = mLocations.get(name); - // We were prepending locations, but we want to prefer the base folders - location = Location.reverse(location); - - // Make sure we still have a conflict, in case one or more of the - // elements were marked with tools:ignore - int count = -1; - LintDriver driver = context.getDriver(); - boolean foundConflict = false; - Location curr; - for (curr = location; curr != null; curr = curr.getSecondary()) { - Object clientData = curr.getClientData(); - if (clientData instanceof Node) { - Node node = (Node) clientData; - if (driver.isSuppressed(INCONSISTENT, node)) { - continue; - } - int newCount = LintUtils.getChildCount(node); - if (newCount != count) { - if (count == -1) { - count = newCount; // first number encountered - } else { - foundConflict = true; - break; - } - } - } else { - foundConflict = true; - break; - } - } - - // Through one or more tools:ignore, there is no more conflict so - // ignore this element - if (!foundConflict) { - continue; - } - - String message = mDescriptions.get(name); - context.report(INCONSISTENT, location, message, null); - } - } - - mLocations = null; - mDescriptions = null; - } - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - int phase = context.getPhase(); - - Attr attribute = element.getAttributeNode(ATTR_NAME); - if (attribute == null || attribute.getValue().isEmpty()) { - if (phase != 1) { - return; - } - context.report(INCONSISTENT, element, context.getLocation(element), - String.format("Missing name attribute in %1$s declaration", element.getTagName()), - null); - } else { - String name = attribute.getValue(); - if (phase == 1) { - if (context.getProject().getReportIssues()) { - int childCount = LintUtils.getChildCount(element); - mFileToArrayCount.put(context.file, Pair.of(name, childCount)); - } - } else { - assert phase == 2; - if (mLocations.containsKey(name)) { - if (context.getDriver().isSuppressed(INCONSISTENT, element)) { - return; - } - Location location = context.getLocation(element); - location.setClientData(element); - location.setMessage(String.format("Declaration with array size (%1$d)", - LintUtils.getChildCount(element))); - location.setSecondary(mLocations.get(name)); - mLocations.put(name, location); - } - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java deleted file mode 100644 index 90ebfd8..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * 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 static com.android.tools.lint.detector.api.LintUtils.assertionsEnabled; -import static com.android.tools.lint.detector.api.LintUtils.endsWith; - -import com.android.annotations.NonNull; -import com.android.annotations.VisibleForTesting; -import com.android.prefs.AndroidLocation; -import com.android.prefs.AndroidLocation.AndroidLocationException; -import com.android.tools.lint.client.api.IssueRegistry; -import com.android.tools.lint.detector.api.Issue; -import com.google.common.annotations.Beta; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -/** Registry which provides a list of checks to be performed on an Android project */ -public class BuiltinIssueRegistry extends IssueRegistry { - /** Folder name in the .android dir where additional detector jars are found */ - private static final String LINT_FOLDER = "lint"; //$NON-NLS-1$ - - /** - * Manifest constant for declaring an issue provider. Example: - * Lint-Registry: foo.bar.CustomIssueRegistry - */ - private static final String MF_LINT_REGISTRY = "Lint-Registry"; //$NON-NLS-1$ - - private static final List<Issue> sIssues; - - static { - final int initialCapacity = 140; - List<Issue> issues = new ArrayList<Issue>(initialCapacity); - - issues.add(AccessibilityDetector.ISSUE); - issues.add(LabelForDetector.ISSUE); - issues.add(MathDetector.ISSUE); - issues.add(FieldGetterDetector.ISSUE); - issues.add(SdCardDetector.ISSUE); - issues.add(ApiDetector.UNSUPPORTED); - issues.add(ApiDetector.INLINED); - issues.add(ApiDetector.OVERRIDE); - issues.add(InvalidPackageDetector.ISSUE); - issues.add(DuplicateIdDetector.CROSS_LAYOUT); - issues.add(DuplicateIdDetector.WITHIN_LAYOUT); - issues.add(DuplicateResourceDetector.ISSUE); - issues.add(WrongIdDetector.UNKNOWN_ID); - issues.add(WrongIdDetector.UNKNOWN_ID_LAYOUT); - issues.add(StateListDetector.ISSUE); - issues.add(StyleCycleDetector.ISSUE); - issues.add(InefficientWeightDetector.INEFFICIENT_WEIGHT); - issues.add(InefficientWeightDetector.NESTED_WEIGHTS); - issues.add(InefficientWeightDetector.BASELINE_WEIGHTS); - issues.add(InefficientWeightDetector.WRONG_0DP); - issues.add(InefficientWeightDetector.ORIENTATION); - issues.add(ScrollViewChildDetector.ISSUE); - issues.add(DeprecationDetector.ISSUE); - issues.add(ObsoleteLayoutParamsDetector.ISSUE); - issues.add(MergeRootFrameLayoutDetector.ISSUE); - issues.add(NestedScrollingWidgetDetector.ISSUE); - issues.add(ChildCountDetector.SCROLLVIEW_ISSUE); - issues.add(ChildCountDetector.ADAPTERVIEW_ISSUE); - issues.add(UseCompoundDrawableDetector.ISSUE); - issues.add(UselessViewDetector.USELESS_PARENT); - issues.add(UselessViewDetector.USELESS_LEAF); - issues.add(TooManyViewsDetector.TOO_MANY); - issues.add(TooManyViewsDetector.TOO_DEEP); - issues.add(GridLayoutDetector.ISSUE); - issues.add(OverrideDetector.ISSUE); - issues.add(OnClickDetector.ISSUE); - issues.add(ViewTagDetector.ISSUE); - issues.add(LocaleDetector.STRING_LOCALE); - issues.add(LocaleDetector.DATE_FORMAT); - issues.add(RegistrationDetector.ISSUE); - issues.add(MissingClassDetector.MISSING); - issues.add(MissingClassDetector.INSTANTIATABLE); - issues.add(MissingClassDetector.INNERCLASS); - issues.add(MissingIdDetector.ISSUE); - issues.add(WrongCaseDetector.WRONGCASE); - issues.add(HandlerDetector.ISSUE); - issues.add(FragmentDetector.ISSUE); - issues.add(TranslationDetector.EXTRA); - issues.add(TranslationDetector.MISSING); - issues.add(HardcodedValuesDetector.ISSUE); - issues.add(Utf8Detector.ISSUE); - issues.add(DosLineEndingDetector.ISSUE); - issues.add(CommentDetector.EASTEREGG); - issues.add(CommentDetector.STOPSHIP); - issues.add(ProguardDetector.WRONGKEEP); - issues.add(ProguardDetector.SPLITCONFIG); - issues.add(PxUsageDetector.PX_ISSUE); - issues.add(PxUsageDetector.DP_ISSUE); - issues.add(PxUsageDetector.IN_MM_ISSUE); - issues.add(PxUsageDetector.SMALL_SP_ISSUE); - issues.add(TextFieldDetector.ISSUE); - issues.add(TextViewDetector.ISSUE); - issues.add(TextViewDetector.SELECTABLE); - issues.add(UnusedResourceDetector.ISSUE); - issues.add(UnusedResourceDetector.ISSUE_IDS); - issues.add(ExtraTextDetector.ISSUE); - issues.add(PrivateResourceDetector.ISSUE); - issues.add(ArraySizeDetector.INCONSISTENT); - issues.add(HardcodedDebugModeDetector.ISSUE); - issues.add(ManifestOrderDetector.ORDER); - issues.add(ManifestOrderDetector.USES_SDK); - issues.add(ManifestOrderDetector.MULTIPLE_USES_SDK); - issues.add(ManifestOrderDetector.WRONG_PARENT); - issues.add(ManifestOrderDetector.DUPLICATE_ACTIVITY); - issues.add(ManifestOrderDetector.TARGET_NEWER); - issues.add(ManifestOrderDetector.ALLOW_BACKUP); - issues.add(ManifestOrderDetector.UNIQUE_PERMISSION); - issues.add(ManifestOrderDetector.SET_VERSION); - issues.add(ManifestOrderDetector.ILLEGAL_REFERENCE); - issues.add(SecurityDetector.EXPORTED_PROVIDER); - issues.add(SecurityDetector.EXPORTED_SERVICE); - issues.add(SecurityDetector.EXPORTED_RECEIVER); - issues.add(SecurityDetector.OPEN_PROVIDER); - issues.add(SecurityDetector.WORLD_READABLE); - issues.add(SecurityDetector.WORLD_WRITEABLE); - issues.add(SecureRandomDetector.ISSUE); - issues.add(IconDetector.GIF_USAGE); - issues.add(IconDetector.ICON_DENSITIES); - issues.add(IconDetector.ICON_MISSING_FOLDER); - issues.add(IconDetector.ICON_DIP_SIZE); - issues.add(IconDetector.ICON_EXPECTED_SIZE); - issues.add(IconDetector.ICON_LOCATION); - issues.add(IconDetector.DUPLICATES_NAMES); - issues.add(IconDetector.DUPLICATES_CONFIGURATIONS); - issues.add(IconDetector.ICON_NODPI); - issues.add(IconDetector.ICON_EXTENSION); - issues.add(IconDetector.ICON_COLORS); - issues.add(IconDetector.ICON_XML_AND_PNG); - issues.add(IconDetector.ICON_LAUNCHER_SHAPE); - issues.add(TypographyDetector.DASHES); - issues.add(TypographyDetector.QUOTES); - issues.add(TypographyDetector.FRACTIONS); - issues.add(TypographyDetector.ELLIPSIS); - issues.add(TypographyDetector.OTHER); - issues.add(ButtonDetector.ORDER); - issues.add(ButtonDetector.CASE); - issues.add(ButtonDetector.BACKBUTTON); - issues.add(ButtonDetector.STYLE); - issues.add(DetectMissingPrefix.MISSING_NAMESPACE); - issues.add(OverdrawDetector.ISSUE); - issues.add(StringFormatDetector.INVALID); - issues.add(StringFormatDetector.ARG_COUNT); - issues.add(StringFormatDetector.ARG_TYPES); - issues.add(TypoDetector.ISSUE); - issues.add(ViewTypeDetector.ISSUE); - issues.add(WrongImportDetector.ISSUE); - issues.add(WrongLocationDetector.ISSUE); - issues.add(ViewConstructorDetector.ISSUE); - issues.add(NamespaceDetector.CUSTOMVIEW); - issues.add(NamespaceDetector.UNUSED); - issues.add(NamespaceDetector.TYPO); - issues.add(AlwaysShowActionDetector.ISSUE); - issues.add(TitleDetector.ISSUE); - issues.add(ColorUsageDetector.ISSUE); - issues.add(JavaPerformanceDetector.PAINT_ALLOC); - issues.add(JavaPerformanceDetector.USE_VALUEOF); - issues.add(JavaPerformanceDetector.USE_SPARSEARRAY); - issues.add(WakelockDetector.ISSUE); - issues.add(CleanupDetector.RECYCLE_RESOURCE); - issues.add(CleanupDetector.COMMIT_FRAGMENT); - issues.add(SetJavaScriptEnabledDetector.ISSUE); - issues.add(ToastDetector.ISSUE); - issues.add(SharedPrefsDetector.ISSUE); - issues.add(CutPasteDetector.ISSUE); - issues.add(NonInternationalizedSmsDetector.ISSUE); - issues.add(PrivateKeyDetector.ISSUE); - issues.add(AnnotationDetector.ISSUE); - issues.add(SystemPermissionsDetector.ISSUE); - issues.add(RequiredAttributeDetector.ISSUE); - issues.add(WrongCallDetector.ISSUE); - - assert initialCapacity >= issues.size() : issues.size(); - - addCustomIssues(issues); - - sIssues = Collections.unmodifiableList(issues); - - // Check that ids are unique - if (assertionsEnabled()) { - Set<String> ids = new HashSet<String>(); - for (Issue issue : sIssues) { - String id = issue.getId(); - assert !ids.contains(id) : "Duplicate id " + id; //$NON-NLS-1$ - ids.add(id); - } - } - } - - /** - * Constructs a new {@link BuiltinIssueRegistry} - */ - public BuiltinIssueRegistry() { - } - - @NonNull - @Override - public List<Issue> getIssues() { - return sIssues; - } - - /** - * Add in custom issues registered by the user - via an environment variable - * or in the .android/lint directory. - */ - private static void addCustomIssues(List<Issue> issues) { - // Look for additional detectors registered by the user, via - // (1) an environment variable (useful for build servers etc), and - // (2) via jar files in the .android/lint directory - Set<File> files = null; - try { - File lint = new File(AndroidLocation.getFolder() + File.separator + LINT_FOLDER); - if (lint.exists()) { - File[] list = lint.listFiles(); - if (list != null) { - for (File jarFile : list) { - if (endsWith(jarFile.getName(), ".jar")) { //$NON-NLS-1$ - if (files == null) { - files = new HashSet<File>(); - } - files.add(jarFile); - addIssuesFromJar(jarFile, issues); - } - } - } - } - } catch (AndroidLocationException e) { - // Ignore -- no android dir, so no rules to load. - } - - String lintClassPath = System.getenv("ANDROID_LINT_JARS"); //$NON-NLS-1$ - if (lintClassPath != null && !lintClassPath.isEmpty()) { - String[] paths = lintClassPath.split(File.pathSeparator); - for (String path : paths) { - File jarFile = new File(path); - if (jarFile.exists() && (files == null || !files.contains(jarFile))) { - addIssuesFromJar(jarFile, issues); - } - } - } - } - - /** Add the issues found in the given jar file into the given list of issues */ - private static void addIssuesFromJar(File jarFile, List<Issue> issues) { - JarFile jarfile = null; - try { - jarfile = new JarFile(jarFile); - Manifest manifest = jarfile.getManifest(); - Attributes attrs = manifest.getMainAttributes(); - Object object = attrs.get(new Attributes.Name(MF_LINT_REGISTRY)); - if (object instanceof String) { - String className = (String) object; - - // Make a class loader for this jar - try { - URL url = jarFile.toURI().toURL(); - URLClassLoader loader = new URLClassLoader(new URL[] { url }, - BuiltinIssueRegistry.class.getClassLoader()); - try { - Class<?> registryClass = Class.forName(className, true, loader); - IssueRegistry registry = (IssueRegistry) registryClass.newInstance(); - for (Issue issue : registry.getIssues()) { - issues.add(issue); - } - } catch (Throwable e) { - log(e); - } - } catch (MalformedURLException e) { - log(e); - } - } - } catch (IOException e) { - log(e); - } finally { - if (jarfile != null) { - try { - jarfile.close(); - } catch (IOException e) { - // Nothing to be done - } - } - } - } - - private static void log(Throwable e) { - // TODO: Where do we log this? There's no embedding tool context here. For now, - // just dump to the console so detector developers get some feedback on what went - // wrong. - e.printStackTrace(); - } - - private static Set<Issue> sAdtFixes; - - /** - * Returns true if the given issue has an automatic IDE fix. - * - * @param tool the name of the tool to be checked - * @param issue the issue to be checked - * @return true if the given tool is known to have an automatic fix for the - * given issue - */ - @Beta - public boolean hasAutoFix(String tool, Issue issue) { - assert tool.equals("adt"); // This is not yet a generic facility; - // the primary purpose right now is to allow for example the HTML report - // to give a hint to the user that some fixes don't require manual work - - return getIssuesWithFixes().contains(issue); - } - - private static Set<Issue> getIssuesWithFixes() { - if (sAdtFixes == null) { - sAdtFixes = new HashSet<Issue>(25); - sAdtFixes.add(InefficientWeightDetector.INEFFICIENT_WEIGHT); - sAdtFixes.add(AccessibilityDetector.ISSUE); - sAdtFixes.add(InefficientWeightDetector.BASELINE_WEIGHTS); - sAdtFixes.add(HardcodedValuesDetector.ISSUE); - sAdtFixes.add(UselessViewDetector.USELESS_LEAF); - sAdtFixes.add(UselessViewDetector.USELESS_PARENT); - sAdtFixes.add(PxUsageDetector.PX_ISSUE); - sAdtFixes.add(TextFieldDetector.ISSUE); - sAdtFixes.add(SecurityDetector.EXPORTED_SERVICE); - sAdtFixes.add(DetectMissingPrefix.MISSING_NAMESPACE); - sAdtFixes.add(ScrollViewChildDetector.ISSUE); - sAdtFixes.add(ObsoleteLayoutParamsDetector.ISSUE); - sAdtFixes.add(TypographyDetector.DASHES); - sAdtFixes.add(TypographyDetector.ELLIPSIS); - sAdtFixes.add(TypographyDetector.FRACTIONS); - sAdtFixes.add(TypographyDetector.OTHER); - sAdtFixes.add(TypographyDetector.QUOTES); - sAdtFixes.add(UseCompoundDrawableDetector.ISSUE); - sAdtFixes.add(ApiDetector.UNSUPPORTED); - sAdtFixes.add(TypoDetector.ISSUE); - sAdtFixes.add(ManifestOrderDetector.ALLOW_BACKUP); - sAdtFixes.add(MissingIdDetector.ISSUE); - sAdtFixes.add(TranslationDetector.MISSING); - sAdtFixes.add(DosLineEndingDetector.ISSUE); - } - - return sAdtFixes; - } - - /** - * Reset the registry such that it recomputes its available issues. - * <p> - * NOTE: This is only intended for testing purposes. - */ - @VisibleForTesting - public static void reset() { - IssueRegistry.reset(); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java deleted file mode 100644 index e756e03..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java +++ /dev/null @@ -1,782 +0,0 @@ -/* - * 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.ANDROID_STRING_PREFIX; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_BACKGROUND; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_TO_LEFT_OF; -import static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_ORIENTATION; -import static com.android.SdkConstants.ATTR_STYLE; -import static com.android.SdkConstants.ATTR_TEXT; -import static com.android.SdkConstants.BUTTON; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.RELATIVE_LAYOUT; -import static com.android.SdkConstants.STRING_PREFIX; -import static com.android.SdkConstants.TABLE_ROW; -import static com.android.SdkConstants.TAG_STRING; -import static com.android.SdkConstants.VALUE_SELECTABLE_ITEM_BACKGROUND; -import static com.android.SdkConstants.VALUE_TRUE; -import static com.android.SdkConstants.VALUE_VERTICAL; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Check which looks at the order of buttons in dialogs and makes sure that - * "the dismissive action of a dialog is always on the left whereas the affirmative actions - * are on the right." - * <p> - * This only looks for the affirmative and dismissive actions named "OK" and "Cancel"; - * "Cancel" usually works, but the affirmative action often has many other names -- "Done", - * "Send", "Go", etc. - * <p> - * TODO: Perhaps we should look for Yes/No dialogs and suggested they be rephrased as - * Cancel/OK dialogs? Similarly, consider "Abort" a synonym for "Cancel" ? - */ -public class ButtonDetector extends ResourceXmlDetector { - /** Name of cancel value ("Cancel") */ - private static final String CANCEL_LABEL = "Cancel"; - /** Name of OK value ("Cancel") */ - private static final String OK_LABEL = "OK"; - /** Name of Back value ("Back") */ - private static final String BACK_LABEL = "Back"; - - /** Layout text attribute reference to {@code @android:string/ok} */ - private static final String ANDROID_OK_RESOURCE = - ANDROID_STRING_PREFIX + "ok"; //$NON-NLS-1$ - /** Layout text attribute reference to {@code @android:string/cancel} */ - private static final String ANDROID_CANCEL_RESOURCE = - ANDROID_STRING_PREFIX + "cancel"; //$NON-NLS-1$ - - /** The main issue discovered by this detector */ - public static final Issue ORDER = Issue.create( - "ButtonOrder", //$NON-NLS-1$ - "Ensures the dismissive action of a dialog is on the left and affirmative on " + - "the right", - - "According to the Android Design Guide,\n" + - "\n" + - "\"Action buttons are typically Cancel and/or OK, with OK indicating the preferred " + - "or most likely action. However, if the options consist of specific actions such " + - "as Close or Wait rather than a confirmation or cancellation of the action " + - "described in the content, then all the buttons should be active verbs. As a rule, " + - "the dismissive action of a dialog is always on the left whereas the affirmative " + - "actions are on the right.\"\n" + - "\n" + - "This check looks for button bars and buttons which look like cancel buttons, " + - "and makes sure that these are on the left.", - - Category.USABILITY, - 8, - Severity.WARNING, - ButtonDetector.class, - Scope.RESOURCE_FILE_SCOPE) - .setMoreInfo( - "http://developer.android.com/design/building-blocks/dialogs.html"); //$NON-NLS-1$ - - /** The main issue discovered by this detector */ - public static final Issue STYLE = Issue.create( - "ButtonStyle", //$NON-NLS-1$ - "Ensures that buttons in button bars are borderless", - - "Button bars typically use a borderless style for the buttons. Set the " + - "`style=\"?android:attr/buttonBarButtonStyle\"` attribute " + - "on each of the buttons, and set `style=\"?android:attr/buttonBarStyle\"` on " + - "the parent layout", - - Category.USABILITY, - 5, - Severity.WARNING, - ButtonDetector.class, - Scope.RESOURCE_FILE_SCOPE) - .setMoreInfo( - "http://developer.android.com/design/building-blocks/buttons.html"); //$NON-NLS-1$ - - /** The main issue discovered by this detector */ - public static final Issue BACKBUTTON = Issue.create( - "BackButton", //$NON-NLS-1$ - "Looks for Back buttons, which are not common on the Android platform.", - // TODO: Look for ">" as label suffixes as well - - "According to the Android Design Guide,\n" + - "\n" + - "\"Other platforms use an explicit back button with label to allow the user " + - "to navigate up the application's hierarchy. Instead, Android uses the main " + - "action bar's app icon for hierarchical navigation and the navigation bar's " + - "back button for temporal navigation.\"" + - "\n" + - "This check is not very sophisticated (it just looks for buttons with the " + - "label \"Back\"), so it is disabled by default to not trigger on common " + - "scenarios like pairs of Back/Next buttons to paginate through screens.", - - Category.USABILITY, - 6, - Severity.WARNING, - ButtonDetector.class, - Scope.RESOURCE_FILE_SCOPE) - .setEnabledByDefault(false) - .setMoreInfo( - "http://developer.android.com/design/patterns/pure-android.html"); //$NON-NLS-1$ - - /** The main issue discovered by this detector */ - public static final Issue CASE = Issue.create( - "ButtonCase", //$NON-NLS-1$ - "Ensures that Cancel/OK dialog buttons use the canonical capitalization", - - "The standard capitalization for OK/Cancel dialogs is \"OK\" and \"Cancel\". " + - "To ensure that your dialogs use the standard strings, you can use " + - "the resource strings @android:string/ok and @android:string/cancel.", - - Category.USABILITY, - 2, - Severity.WARNING, - ButtonDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Set of resource names whose value was either OK or Cancel */ - private Set<String> mApplicableResources; - - /** - * Map of resource names we'd like resolved into strings in phase 2. The - * values should be filled in with the actual string contents. - */ - private Map<String, String> mKeyToLabel; - - /** - * Set of elements we've already warned about. If we've already complained - * about a cancel button, don't also report the OK button (since it's listed - * for the warnings on OK buttons). - */ - private Set<Element> mIgnore; - - /** Constructs a new {@link ButtonDetector} */ - public ButtonDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList(BUTTON, TAG_STRING); - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.LAYOUT || folderType == ResourceFolderType.VALUES; - } - - @Override - public void afterCheckProject(@NonNull Context context) { - int phase = context.getPhase(); - if (phase == 1 && mApplicableResources != null) { - // We found resources for the string "Cancel"; perform a second pass - // where we check layout text attributes against these strings. - context.getDriver().requestRepeat(this, Scope.RESOURCE_FILE_SCOPE); - } - } - - private static String stripLabel(String text) { - text = text.trim(); - if (text.length() > 2 - && (text.charAt(0) == '"' || text.charAt(0) == '\'') - && (text.charAt(0) == text.charAt(text.length() - 1))) { - text = text.substring(1, text.length() - 1); - } - - return text; - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - // This detector works in two passes. - // In pass 1, it looks in layout files for hardcoded strings of "Cancel", or - // references to @string/cancel or @android:string/cancel. - // It also looks in values/ files for strings whose value is "Cancel", - // and if found, stores the corresponding keys in a map. (This is necessary - // since value files are processed after layout files). - // Then, if at the end of phase 1 any "Cancel" string resources were - // found in the value files, then it requests a *second* phase, - // where it looks only for <Button>'s whose text matches one of the - // cancel string resources. - int phase = context.getPhase(); - String tagName = element.getTagName(); - if (phase == 1 && tagName.equals(TAG_STRING)) { - NodeList childNodes = element.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.TEXT_NODE) { - String text = child.getNodeValue(); - for (int j = 0, len = text.length(); j < len; j++) { - char c = text.charAt(j); - if (!Character.isWhitespace(c)) { - if (c == '"' || c == '\'') { - continue; - } - if (LintUtils.startsWith(text, CANCEL_LABEL, j)) { - String label = stripLabel(text); - if (label.equalsIgnoreCase(CANCEL_LABEL)) { - String name = element.getAttribute(ATTR_NAME); - foundResource(context, name, element); - - if (!label.equals(CANCEL_LABEL) - && isEnglishResource(context) - && context.isEnabled(CASE)) { - assert label.equalsIgnoreCase(CANCEL_LABEL); - context.report(CASE, child, context.getLocation(child), - String.format( - "The standard Android way to capitalize %1$s " + - "is \"Cancel\" (tip: use @android:string/ok instead)", - label), null); - } - } - } else if (LintUtils.startsWith(text, OK_LABEL, j)) { - String label = stripLabel(text); - if (label.equalsIgnoreCase(OK_LABEL)) { - String name = element.getAttribute(ATTR_NAME); - foundResource(context, name, element); - - if (!label.equals(OK_LABEL) - && isEnglishResource(context) - && context.isEnabled(CASE)) { - assert text.trim().equalsIgnoreCase(OK_LABEL); - context.report(CASE, child, context.getLocation(child), - String.format( - "The standard Android way to capitalize %1$s " + - "is \"OK\" (tip: use @android:string/ok instead)", - label), null); - } - } - } else if (LintUtils.startsWith(text, BACK_LABEL, j) && - stripLabel(text).equalsIgnoreCase(BACK_LABEL)) { - String name = element.getAttribute(ATTR_NAME); - foundResource(context, name, element); - } - break; - } - } - } - } - } else if (tagName.equals(BUTTON)) { - if (phase == 1) { - if (isInButtonBar(element) - && !element.hasAttribute(ATTR_STYLE) - && !VALUE_SELECTABLE_ITEM_BACKGROUND.equals( - element.getAttributeNS(ANDROID_URI, ATTR_BACKGROUND)) - && (context.getProject().getMinSdk() >= 11 - || context.getFolderVersion() >= 11) - && context.isEnabled(STYLE) - && !parentDefinesSelectableItem(element)) { - context.report(STYLE, element, context.getLocation(element), - "Buttons in button bars should be borderless; use " + - "style=\"?android:attr/buttonBarButtonStyle\" (and " + - "?android:attr/buttonBarStyle on the parent)", - null); - } - } - - String text = element.getAttributeNS(ANDROID_URI, ATTR_TEXT); - if (phase == 2) { - if (mApplicableResources.contains(text)) { - String key = text; - if (key.startsWith(STRING_PREFIX)) { - key = key.substring(STRING_PREFIX.length()); - } - String label = mKeyToLabel.get(key); - boolean isCancel = CANCEL_LABEL.equalsIgnoreCase(label); - if (isCancel) { - if (isWrongCancelPosition(element)) { - reportCancelPosition(context, element); - } - } else if (OK_LABEL.equalsIgnoreCase(label)) { - if (isWrongOkPosition(element)) { - reportOkPosition(context, element); - } - } else { - assert BACK_LABEL.equalsIgnoreCase(label) : label + ':' + context.file; - Location location = context.getLocation(element); - if (context.isEnabled(BACKBUTTON)) { - context.report(BACKBUTTON, element, location, - "Back buttons are not standard on Android; see design guide's " + - "navigation section", null); - } - } - } - } else if (text.equals(CANCEL_LABEL) || text.equals(ANDROID_CANCEL_RESOURCE)) { - if (isWrongCancelPosition(element)) { - reportCancelPosition(context, element); - } - } else if (text.equals(OK_LABEL) || text.equals(ANDROID_OK_RESOURCE)) { - if (isWrongOkPosition(element)) { - reportOkPosition(context, element); - } - } - } - } - - private static boolean parentDefinesSelectableItem(Element element) { - String background = element.getAttributeNS(ANDROID_URI, ATTR_BACKGROUND); - if (VALUE_SELECTABLE_ITEM_BACKGROUND.equals(background)) { - return true; - } - - Node parent = element.getParentNode(); - if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) { - return parentDefinesSelectableItem((Element) parent); - } - - return false; - } - - /** Report the given OK button as being in the wrong position */ - private void reportOkPosition(XmlContext context, Element element) { - report(context, element, false /*isCancel*/); - } - - /** Report the given Cancel button as being in the wrong position */ - private void reportCancelPosition(XmlContext context, Element element) { - report(context, element, true /*isCancel*/); - } - - - /** The Ok/Cancel detector only works with default and English locales currently. - * TODO: Add in patterns for other languages. We can use the - * @android:string/ok and @android:string/cancel localizations to look - * up the canonical ones. */ - private static boolean isEnglishResource(XmlContext context) { - String folder = context.file.getParentFile().getName(); - if (folder.indexOf('-') != -1) { - String[] qualifiers = folder.split("-"); //$NON-NLS-1$ - for (String qualifier : qualifiers) { - if (qualifier.equals("en")) { //$NON-NLS-1$ - return true; - } - } - return false; - } - - // Default folder ("values") - may not be English but we'll consider matches - // on "OK", "Cancel" and "Back" as matches there - return true; - } - - /** - * We've found a resource reference to some label we're interested in ("OK", - * "Cancel", "Back", ...). Record the corresponding name such that in the - * next pass through the layouts we can check the context (for OK/Cancel the - * button order etc). - */ - private void foundResource(XmlContext context, String name, Element element) { - if (!isEnglishResource(context)) { - return; - } - - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - if (mApplicableResources == null) { - mApplicableResources = new HashSet<String>(); - } - - mApplicableResources.add(STRING_PREFIX + name); - - // ALSO record all the other string resources in this file to pick up other - // labels. If you define "OK" in one resource file and "Cancel" in another - // this won't work, but that's probably not common and has lower overhead. - Node parentNode = element.getParentNode(); - - List<Element> items = LintUtils.getChildren(parentNode); - if (mKeyToLabel == null) { - mKeyToLabel = new HashMap<String, String>(items.size()); - } - for (Element item : items) { - String itemName = item.getAttribute(ATTR_NAME); - NodeList childNodes = item.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.TEXT_NODE) { - String text = stripLabel(child.getNodeValue()); - if (!text.isEmpty()) { - mKeyToLabel.put(itemName, text); - break; - } - } - } - } - } - - /** Report the given OK/Cancel button as being in the wrong position */ - private void report(XmlContext context, Element element, boolean isCancel) { - if (!context.isEnabled(ORDER)) { - return; - } - - if (mIgnore != null && mIgnore.contains(element)) { - return; - } - - int target = context.getProject().getTargetSdk(); - if (target < 14) { - // If you're only targeting pre-ICS UI's, this is not an issue - return; - } - - boolean mustCreateIcsLayout = false; - if (context.getProject().getMinSdk() < 14) { - // If you're *also* targeting pre-ICS UIs, then this reverse button - // order is correct for layouts intended for pre-ICS and incorrect for - // ICS layouts. - // - // Therefore, we need to know if this layout is an ICS layout or - // a pre-ICS layout. - boolean isIcsLayout = context.getFolderVersion() >= 14; - if (!isIcsLayout) { - // This layout is not an ICS layout. However, there *must* also be - // an ICS layout here, or this button order will be wrong: - File res = context.file.getParentFile().getParentFile(); - File[] resFolders = res.listFiles(); - String fileName = context.file.getName(); - if (resFolders != null) { - for (File folder : resFolders) { - String folderName = folder.getName(); - if (folderName.startsWith(SdkConstants.FD_RES_LAYOUT) - && folderName.contains("-v14")) { //$NON-NLS-1$ - File layout = new File(folder, fileName); - if (layout.exists()) { - // Yes, a v14 specific layout is available so this pre-ICS - // layout order is not a problem - return; - } - } - } - } - mustCreateIcsLayout = true; - } - } - - List<Element> buttons = LintUtils.getChildren(element.getParentNode()); - - if (mIgnore == null) { - mIgnore = new HashSet<Element>(); - } - for (Element button : buttons) { - // Mark all the siblings in the ignore list to ensure that we don't - // report *both* the Cancel and the OK button in "OK | Cancel" - mIgnore.add(button); - } - - String message; - if (isCancel) { - message = "Cancel button should be on the left"; - } else { - message = "OK button should be on the right"; - } - - if (mustCreateIcsLayout) { - message = String.format( - "Layout uses the wrong button order for API >= 14: Create a " + - "layout-v14/%1$s file with opposite order: %2$s", - context.file.getName(), message); - } - - // Show existing button order? We can only do that for LinearLayouts - // since in for example a RelativeLayout the order of the elements may - // not be the same as the visual order - String layout = element.getParentNode().getNodeName(); - if (layout.equals(LINEAR_LAYOUT) || layout.equals(TABLE_ROW)) { - List<String> labelList = getLabelList(buttons); - String wrong = describeButtons(labelList); - sortButtons(labelList); - String right = describeButtons(labelList); - message += String.format(" (was \"%1$s\", should be \"%2$s\")", wrong, right); - } - - Location location = context.getLocation(element); - context.report(ORDER, element, location, message, null); - } - - /** - * Sort a list of label buttons into the expected order (Cancel on the left, - * OK on the right - */ - private static void sortButtons(List<String> labelList) { - for (int i = 0, n = labelList.size(); i < n; i++) { - String label = labelList.get(i); - if (label.equalsIgnoreCase(CANCEL_LABEL) && i > 0) { - swap(labelList, 0, i); - } else if (label.equalsIgnoreCase(OK_LABEL) && i < n - 1) { - swap(labelList, n - 1, i); - } - } - } - - /** Swaps the strings at positions i and j */ - private static void swap(List<String> strings, int i, int j) { - if (i != j) { - String temp = strings.get(i); - strings.set(i, strings.get(j)); - strings.set(j, temp); - } - } - - /** Creates a display string for a list of button labels, such as "Cancel | OK" */ - private static String describeButtons(List<String> labelList) { - StringBuilder sb = new StringBuilder(80); - for (String label : labelList) { - if (sb.length() > 0) { - sb.append(" | "); //$NON-NLS-1$ - } - sb.append(label); - } - - return sb.toString(); - } - - /** Returns the ordered list of button labels */ - private List<String> getLabelList(List<Element> views) { - List<String> labels = new ArrayList<String>(); - - if (mIgnore == null) { - mIgnore = new HashSet<Element>(); - } - - for (Element view : views) { - if (view.getTagName().equals(BUTTON)) { - String text = view.getAttributeNS(ANDROID_URI, ATTR_TEXT); - String label = getLabel(text); - labels.add(label); - - // Mark all the siblings in the ignore list to ensure that we don't - // report *both* the Cancel and the OK button in "OK | Cancel" - mIgnore.add(view); - } - } - - return labels; - } - - private String getLabel(String key) { - String label = null; - if (key.startsWith(ANDROID_STRING_PREFIX)) { - if (key.equals(ANDROID_OK_RESOURCE)) { - label = OK_LABEL; - } else if (key.equals(ANDROID_CANCEL_RESOURCE)) { - label = CANCEL_LABEL; - } - } else if (mKeyToLabel != null) { - if (key.startsWith(STRING_PREFIX)) { - label = mKeyToLabel.get(key.substring(STRING_PREFIX.length())); - } - } - - if (label == null) { - label = key; - } - - if (label.indexOf(' ') != -1 && label.indexOf('"') == -1) { - label = '"' + label + '"'; - } - - return label; - } - - /** Is the cancel button in the wrong position? It has to be on the left. */ - private boolean isWrongCancelPosition(Element element) { - return isWrongPosition(element, true /*isCancel*/); - } - - /** Is the OK button in the wrong position? It has to be on the right. */ - private boolean isWrongOkPosition(Element element) { - return isWrongPosition(element, false /*isCancel*/); - } - - private static boolean isInButtonBar(Element element) { - assert element.getTagName().equals(BUTTON) : element.getTagName(); - Node parentNode = element.getParentNode(); - if (parentNode.getNodeType() != Node.ELEMENT_NODE) { - return false; - } - Element parent = (Element) parentNode; - - String style = parent.getAttribute(ATTR_STYLE); - if (style != null && style.contains("buttonBarStyle")) { //$NON-NLS-1$ - return true; - } - - // Don't warn about single Cancel / OK buttons - if (LintUtils.getChildCount(parent) < 2) { - return false; - } - - String layout = parent.getTagName(); - if (layout.equals(LINEAR_LAYOUT) || layout.equals(TABLE_ROW)) { - String orientation = parent.getAttributeNS(ANDROID_URI, ATTR_ORIENTATION); - if (VALUE_VERTICAL.equals(orientation)) { - return false; - } - } else { - return false; - } - - // Ensure that all the children are buttons - Node n = parent.getFirstChild(); - while (n != null) { - if (n.getNodeType() == Node.ELEMENT_NODE) { - if (!BUTTON.equals(n.getNodeName())) { - return false; - } - } - n = n.getNextSibling(); - } - - return true; - } - - /** Is the given button in the wrong position? */ - private static boolean isWrongPosition(Element element, boolean isCancel) { - Node parentNode = element.getParentNode(); - if (parentNode.getNodeType() != Node.ELEMENT_NODE) { - return false; - } - Element parent = (Element) parentNode; - - // Don't warn about single Cancel / OK buttons - if (LintUtils.getChildCount(parent) < 2) { - return false; - } - - String layout = parent.getTagName(); - if (layout.equals(LINEAR_LAYOUT) || layout.equals(TABLE_ROW)) { - String orientation = parent.getAttributeNS(ANDROID_URI, ATTR_ORIENTATION); - if (VALUE_VERTICAL.equals(orientation)) { - return false; - } - - if (isCancel) { - Node n = element.getPreviousSibling(); - while (n != null) { - if (n.getNodeType() == Node.ELEMENT_NODE) { - return true; - } - n = n.getPreviousSibling(); - } - } else { - Node n = element.getNextSibling(); - while (n != null) { - if (n.getNodeType() == Node.ELEMENT_NODE) { - return true; - } - n = n.getNextSibling(); - } - } - - return false; - } else if (layout.equals(RELATIVE_LAYOUT)) { - // In RelativeLayouts, look for attachments which look like a clear sign - // that the OK or Cancel buttons are out of order: - // -- a left attachment on a Cancel button (where the left attachment - // is a button; we don't want to complain if it's pointing to a spacer - // or image or progress indicator etc) - // -- a right-side parent attachment on a Cancel button (unless it's also - // attached on the left, e.g. a cancel button stretching across the - // layout) - // etc. - if (isCancel) { - if (element.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_TO_RIGHT_OF) - && isButtonId(parent, element.getAttributeNS(ANDROID_URI, - ATTR_LAYOUT_TO_RIGHT_OF))) { - return true; - } - if (isTrue(element, ATTR_LAYOUT_ALIGN_PARENT_RIGHT) && - !isTrue(element, ATTR_LAYOUT_ALIGN_PARENT_LEFT)) { - return true; - } - } else { - if (element.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_TO_LEFT_OF) - && isButtonId(parent, element.getAttributeNS(ANDROID_URI, - ATTR_LAYOUT_TO_RIGHT_OF))) { - return true; - } - if (isTrue(element, ATTR_LAYOUT_ALIGN_PARENT_LEFT) && - !isTrue(element, ATTR_LAYOUT_ALIGN_PARENT_RIGHT)) { - return true; - } - } - - return false; - } else { - // TODO: Consider other button layouts - GridLayouts, custom views extending - // LinearLayout etc? - return false; - } - } - - /** - * Returns true if the given attribute (in the Android namespace) is set to - * true on the given element - */ - private static boolean isTrue(Element element, String attribute) { - return VALUE_TRUE.equals(element.getAttributeNS(ANDROID_URI, attribute)); - } - - /** Is the given target id the id of a {@code <Button>} within this RelativeLayout? */ - private static boolean isButtonId(Element parent, String targetId) { - for (Element child : LintUtils.getChildren(parent)) { - String id = child.getAttributeNS(ANDROID_URI, ATTR_ID); - if (LintUtils.idReferencesMatch(id, targetId)) { - return child.getTagName().equals(BUTTON); - } - } - return false; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ChildCountDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ChildCountDetector.java deleted file mode 100644 index bc1e30d..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ChildCountDetector.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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 static com.android.SdkConstants.GRID_VIEW; -import static com.android.SdkConstants.HORIZONTAL_SCROLL_VIEW; -import static com.android.SdkConstants.LIST_VIEW; -import static com.android.SdkConstants.REQUEST_FOCUS; -import static com.android.SdkConstants.SCROLL_VIEW; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -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 org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.Arrays; -import java.util.Collection; - -/** - * Check which makes sure that views have the expected number of declared - * children (e.g. at most one in ScrollViews and none in AdapterViews) - */ -public class ChildCountDetector extends LayoutDetector { - - /** The main issue discovered by this detector */ - public static final Issue SCROLLVIEW_ISSUE = Issue.create( - "ScrollViewCount", //$NON-NLS-1$ - "Checks that ScrollViews have exactly one child widget", - "ScrollViews can only have one child widget. If you want more children, wrap them " + - "in a container layout.", - Category.CORRECTNESS, - 8, - Severity.WARNING, - ChildCountDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** The main issue discovered by this detector */ - public static final Issue ADAPTERVIEW_ISSUE = Issue.create( - "AdapterViewChildren", //$NON-NLS-1$ - "Checks that AdapterViews do not define their children in XML", - "AdapterViews such as ListViews must be configured with data from Java code, " + - "such as a ListAdapter.", - Category.CORRECTNESS, - 10, - Severity.WARNING, - ChildCountDetector.class, - Scope.RESOURCE_FILE_SCOPE).setMoreInfo( - "http://developer.android.com/reference/android/widget/AdapterView.html"); //$NON-NLS-1$ - - /** Constructs a new {@link ChildCountDetector} */ - public ChildCountDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - SCROLL_VIEW, - HORIZONTAL_SCROLL_VIEW, - LIST_VIEW, - GRID_VIEW - // TODO: Shouldn't Spinner be in this list too? (Was not there in layoutopt) - ); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - int childCount = LintUtils.getChildCount(element); - String tagName = element.getTagName(); - if (tagName.equals(SCROLL_VIEW) || tagName.equals(HORIZONTAL_SCROLL_VIEW)) { - if (childCount > 1 && getAccurateChildCount(element) > 1) { - context.report(SCROLLVIEW_ISSUE, element, - context.getLocation(element), "A scroll view can have only one child", - null); - } - } else { - // Adapter view - if (childCount > 0 && getAccurateChildCount(element) > 0) { - context.report(ADAPTERVIEW_ISSUE, element, - context.getLocation(element), - "A list/grid should have no children declared in XML", null); - } - } - } - - /** Counts the number of children, but skips certain tags like {@code <requestFocus>} */ - private static int getAccurateChildCount(Element element) { - NodeList childNodes = element.getChildNodes(); - int childCount = 0; - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE && - !REQUEST_FOCUS.equals(child.getNodeName())) { - childCount++; - } - } - - return childCount; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/CleanupDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/CleanupDetector.java deleted file mode 100644 index 1541d23..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/CleanupDetector.java +++ /dev/null @@ -1,540 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.annotations.VisibleForTesting; -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.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 org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.analysis.Analyzer; -import org.objectweb.asm.tree.analysis.AnalyzerException; -import org.objectweb.asm.tree.analysis.BasicValue; -import org.objectweb.asm.tree.analysis.Frame; -import org.objectweb.asm.tree.analysis.Interpreter; -import org.objectweb.asm.tree.analysis.Value; - -import java.util.Arrays; -import java.util.List; - -/** - * Checks for missing {@code recycle} calls on resources that encourage it, and - * for missing {@code commit} calls on FragmentTransactions, etc. - */ -public class CleanupDetector extends Detector implements ClassScanner { - /** Problems with missing recycle calls */ - public static final Issue RECYCLE_RESOURCE = Issue.create( - "Recycle", //$NON-NLS-1$ - "Looks for missing recycle() calls on resources", - - "Many resources, such as TypedArrays, VelocityTrackers, etc., " + - "should be recycled (with a `recycle()` call) after use. This lint check looks " + - "for missing `recycle()` calls.", - - Category.PERFORMANCE, - 7, - Severity.WARNING, - CleanupDetector.class, - Scope.CLASS_FILE_SCOPE); - - /** Problems with missing commit calls. */ - public static final Issue COMMIT_FRAGMENT = Issue.create( - "CommitTransaction", //$NON-NLS-1$ - "Looks for missing commit() calls on FragmentTransactions", - - "After creating a `FragmentTransaction`, you typically need to commit it as well", - - Category.CORRECTNESS, - 7, - Severity.WARNING, - CleanupDetector.class, - Scope.CLASS_FILE_SCOPE); - - // Target method names - private static final String RECYCLE = "recycle"; //$NON-NLS-1$ - private static final String OBTAIN = "obtain"; //$NON-NLS-1$ - private static final String OBTAIN_NO_HISTORY = "obtainNoHistory"; //$NON-NLS-1$ - private static final String OBTAIN_ATTRIBUTES = "obtainAttributes"; //$NON-NLS-1$ - private static final String OBTAIN_TYPED_ARRAY = "obtainTypedArray"; //$NON-NLS-1$ - private static final String OBTAIN_STYLED_ATTRIBUTES = "obtainStyledAttributes"; //$NON-NLS-1$ - private static final String BEGIN_TRANSACTION = "beginTransaction"; //$NON-NLS-1$ - private static final String COMMIT = "commit"; //$NON-NLS-1$ - private static final String COMMIT_ALLOWING_LOSS = "commitAllowingStateLoss"; //$NON-NLS-1$ - - // Target owners - private static final String VELOCITY_TRACKER_CLS = "android/view/VelocityTracker";//$NON-NLS-1$ - private static final String TYPED_ARRAY_CLS = "android/content/res/TypedArray"; //$NON-NLS-1$ - private static final String CONTEXT_CLS = "android/content/Context"; //$NON-NLS-1$ - private static final String MOTION_EVENT_CLS = "android/view/MotionEvent"; //$NON-NLS-1$ - private static final String HANDLER_CLS = "android/os/Handler"; //$NON-NLS-1$ - private static final String RESOURCES_CLS = "android/content/res/Resources"; //$NON-NLS-1$ - private static final String PARCEL_CLS = "android/os/Parcel"; //$NON-NLS-1$ - private static final String FRAGMENT_MANAGER_CLS = "android/app/FragmentManager"; //$NON-NLS-1$ - private static final String FRAGMENT_MANAGER_V4_CLS = "android/support/v4/app/FragmentManager"; //$NON-NLS-1$ - private static final String FRAGMENT_TRANSACTION_CLS = "android/app/FragmentTransaction"; //$NON-NLS-1$ - private static final String FRAGMENT_TRANSACTION_V4_CLS = "android/support/v4/app/FragmentTransaction"; //$NON-NLS-1$ - - // Target description signatures - private static final String TYPED_ARRAY_SIG = "Landroid/content/res/TypedArray;"; //$NON-NLS-1$ - - private boolean mObtainsTypedArray; - private boolean mRecyclesTypedArray; - private boolean mObtainsTracker; - private boolean mRecyclesTracker; - private boolean mObtainsMotionEvent; - private boolean mRecyclesMotionEvent; - private boolean mObtainsParcel; - private boolean mRecyclesParcel; - private boolean mObtainsTransaction; - private boolean mCommitsTransaction; - - /** Constructs a new {@link CleanupDetector} */ - public CleanupDetector() { - } - - @Override - public void afterCheckProject(@NonNull Context context) { - int phase = context.getDriver().getPhase(); - if (phase == 1) { - if (mObtainsTypedArray && !mRecyclesTypedArray - || mObtainsTracker && !mRecyclesTracker - || mObtainsParcel && !mRecyclesParcel - || mObtainsMotionEvent && !mRecyclesMotionEvent - || mObtainsTransaction && !mCommitsTransaction) { - context.getDriver().requestRepeat(this, Scope.CLASS_FILE_SCOPE); - } - } - } - - // ---- Implements ClassScanner ---- - - @Override - @Nullable - public List<String> getApplicableCallNames() { - return Arrays.asList( - RECYCLE, - OBTAIN_STYLED_ATTRIBUTES, - OBTAIN, - OBTAIN_ATTRIBUTES, - OBTAIN_TYPED_ARRAY, - OBTAIN_NO_HISTORY, - BEGIN_TRANSACTION, - COMMIT, - COMMIT_ALLOWING_LOSS - ); - } - - @Override - public void checkCall( - @NonNull ClassContext context, - @NonNull ClassNode classNode, - @NonNull MethodNode method, - @NonNull MethodInsnNode call) { - String name = call.name; - String owner = call.owner; - String desc = call.desc; - int phase = context.getDriver().getPhase(); - if (RECYCLE.equals(name) && desc.equals("()V")) { //$NON-NLS-1$ - if (owner.equals(TYPED_ARRAY_CLS)) { - mRecyclesTypedArray = true; - } else if (owner.equals(VELOCITY_TRACKER_CLS)) { - mRecyclesTracker = true; - } else if (owner.equals(MOTION_EVENT_CLS)) { - mRecyclesMotionEvent = true; - } else if (owner.equals(PARCEL_CLS)) { - mRecyclesParcel = true; - } - } else if ((COMMIT.equals(name) || COMMIT_ALLOWING_LOSS.equals(name)) - && desc.equals("()I")) { //$NON-NLS-1$ - if (owner.equals(FRAGMENT_TRANSACTION_CLS) - || owner.equals(FRAGMENT_TRANSACTION_V4_CLS)) { - mCommitsTransaction = true; - } - } else if (owner.equals(MOTION_EVENT_CLS)) { - if (OBTAIN.equals(name) || OBTAIN_NO_HISTORY.equals(name)) { - mObtainsMotionEvent = true; - if (phase == 2 && !mRecyclesMotionEvent) { - context.report(RECYCLE_RESOURCE, method, call, context.getLocation(call), - getErrorMessage(MOTION_EVENT_CLS), - null); - } else if (phase == 1 - && checkMethodFlow(context, classNode, method, call, MOTION_EVENT_CLS)) { - // Already reported error above; don't do global check - mRecyclesMotionEvent = true; - } - } - } else if (OBTAIN.equals(name)) { - if (owner.equals(VELOCITY_TRACKER_CLS)) { - mObtainsTracker = true; - if (phase == 2 && !mRecyclesTracker) { - context.report(RECYCLE_RESOURCE, method, call, context.getLocation(call), - getErrorMessage(VELOCITY_TRACKER_CLS), - null); - } - } else if (owner.equals(PARCEL_CLS)) { - mObtainsParcel = true; - if (phase == 2 && !mRecyclesParcel) { - context.report(RECYCLE_RESOURCE, method, call, context.getLocation(call), - getErrorMessage(PARCEL_CLS), - null); - } else if (phase == 1 - && checkMethodFlow(context, classNode, method, call, PARCEL_CLS)) { - // Already reported error above; don't do global check - mRecyclesParcel = true; - } - } - } else if (OBTAIN_STYLED_ATTRIBUTES.equals(name) - || OBTAIN_ATTRIBUTES.equals(name) - || OBTAIN_TYPED_ARRAY.equals(name)) { - if ((owner.equals(CONTEXT_CLS) || owner.equals(RESOURCES_CLS)) - && desc.endsWith(TYPED_ARRAY_SIG)) { - mObtainsTypedArray = true; - if (phase == 2 && !mRecyclesTypedArray) { - context.report(RECYCLE_RESOURCE, method, call, context.getLocation(call), - getErrorMessage(TYPED_ARRAY_CLS), - null); - } else if (phase == 1 - && checkMethodFlow(context, classNode, method, call, TYPED_ARRAY_CLS)) { - // Already reported error above; don't do global check - mRecyclesTypedArray = true; - } - } - } else if (BEGIN_TRANSACTION.equals(name) - && (owner.equals(FRAGMENT_MANAGER_CLS) || owner.equals(FRAGMENT_MANAGER_V4_CLS))) { - mObtainsTransaction = true; - if (phase == 2 && !mCommitsTransaction) { - context.report(COMMIT_FRAGMENT, method, call, context.getLocation(call), - getErrorMessage(FRAGMENT_MANAGER_CLS), null); - } else if (phase == 1 - && checkMethodFlow(context, classNode, method, call, - owner.equals(FRAGMENT_MANAGER_CLS) - ? FRAGMENT_TRANSACTION_CLS : FRAGMENT_TRANSACTION_V4_CLS)) { - // Already reported error above; don't do global check - mCommitsTransaction = true; - } - } - } - - /** Computes an error message for a missing recycle of the given type */ - private static String getErrorMessage(String owner) { - if (FRAGMENT_TRANSACTION_CLS.equals(owner) || FRAGMENT_TRANSACTION_V4_CLS.equals(owner)) { - return "This transaction should be completed with a commit() call"; - } - String className = owner.substring(owner.lastIndexOf('/') + 1); - return String.format("This %1$s should be recycled after use with #recycle()", - className); - } - - /** - * Ensures that the given allocate call in the given method has a - * corresponding recycle method, also within the same method, OR, the - * allocated resource flows out of the method (either as a return value, or - * into a field, or into some other method (with some known exceptions; e.g. - * passing a MotionEvent into another MotionEvent's constructor is fine) - * <p> - * Returns true if an error was found - */ - private static boolean checkMethodFlow(ClassContext context, ClassNode classNode, - MethodNode method, MethodInsnNode call, String recycleOwner) { - CleanupTracker interpreter = new CleanupTracker(context, method, call, recycleOwner); - ResourceAnalyzer analyzer = new ResourceAnalyzer(interpreter); - interpreter.setAnalyzer(analyzer); - try { - analyzer.analyze(classNode.name, method); - if (!interpreter.isCleanedUp() && !interpreter.isEscaped()) { - Location location = context.getLocation(call); - String message = getErrorMessage(recycleOwner); - Issue issue = call.owner.equals(FRAGMENT_MANAGER_CLS) - ? COMMIT_FRAGMENT : RECYCLE_RESOURCE; - context.report(issue, method, call, location, message, null); - return true; - } - } catch (AnalyzerException e) { - context.log(e, null); - } - - return false; - } - - @VisibleForTesting - static boolean hasReturnType(String owner, String desc) { - int descLen = desc.length(); - int ownerLen = owner.length(); - if (descLen < ownerLen + 3) { - return false; - } - if (desc.charAt(descLen - 1) != ';') { - return false; - } - int typeBegin = descLen - 2 - ownerLen; - if (desc.charAt(typeBegin - 1) != ')' || desc.charAt(typeBegin) != 'L') { - return false; - } - return desc.regionMatches(typeBegin + 1, owner, 0, ownerLen); - } - - /** - * ASM interpreter which tracks the instances of the allocated resource, and - * checks whether it is eventually passed to a {@code recycle()} call. If the - * value flows out of the method (to a field, or a method call), it will - * also consider the resource recycled. - */ - private static class CleanupTracker extends Interpreter { - // Only identity matters, not value - private static final Value INSTANCE = BasicValue.INT_VALUE; - private static final Value RECYCLED = BasicValue.FLOAT_VALUE; - private static final Value UNKNOWN = BasicValue.UNINITIALIZED_VALUE; - - private final ClassContext mContext; - private final MethodNode mMethod; - private final MethodInsnNode mObtainNode; - private boolean mIsCleanedUp; - private boolean mEscapes; - private final String mRecycleOwner; - private ResourceAnalyzer mAnalyzer; - - public CleanupTracker( - @NonNull ClassContext context, - @NonNull MethodNode method, - @NonNull MethodInsnNode obtainNode, - @NonNull String recycleOwner) { - super(Opcodes.ASM4); - mContext = context; - mMethod = method; - mObtainNode = obtainNode; - mRecycleOwner = recycleOwner; - } - - /** - * Sets the analyzer associated with the interpreter, such that it can - * get access to the execution frames - */ - void setAnalyzer(ResourceAnalyzer analyzer) { - mAnalyzer = analyzer; - } - - /** - * Returns whether a recycle call was found for the given method - * - * @return true if the resource was recycled - */ - public boolean isCleanedUp() { - return mIsCleanedUp; - } - - /** - * Returns whether the target resource escapes from the method, for - * example as a return value, or a field assignment, or getting passed - * to another method - * - * @return true if the resource escapes - */ - public boolean isEscaped() { - return mEscapes; - } - - @Override - public Value newOperation(AbstractInsnNode node) throws AnalyzerException { - return UNKNOWN; - } - - @Override - public Value newValue(final Type type) { - if (type != null && type.getSort() == Type.VOID) { - return null; - } else { - return UNKNOWN; - } - } - - @Override - public Value copyOperation(AbstractInsnNode node, Value value) throws AnalyzerException { - return value; - } - - @Override - public Value binaryOperation(AbstractInsnNode node, Value value1, Value value2) - throws AnalyzerException { - if (node.getOpcode() == Opcodes.PUTFIELD) { - if (value2 == INSTANCE) { - mEscapes = true; - } - } - return merge(value1, value2); - } - - @Override - public Value naryOperation(AbstractInsnNode node, List values) throws AnalyzerException { - if (node == mObtainNode) { - return INSTANCE; - } - - MethodInsnNode call = null; - if (node.getType() == AbstractInsnNode.METHOD_INSN) { - call = (MethodInsnNode) node; - if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) { - if (call.name.equals(RECYCLE) && call.owner.equals(mRecycleOwner)) { - if (values != null && values.size() == 1 && values.get(0) == INSTANCE) { - mIsCleanedUp = true; - Frame frame = mAnalyzer.getCurrentFrame(); - if (frame != null) { - int localSize = frame.getLocals(); - for (int i = 0; i < localSize; i++) { - Value local = frame.getLocal(i); - if (local == INSTANCE) { - frame.setLocal(i, RECYCLED); - } - } - int stackSize = frame.getStackSize(); - if (stackSize == 1 && frame.getStack(0) == INSTANCE) { - frame.pop(); - frame.push(RECYCLED); - } - } - return RECYCLED; - } - } else if ((call.name.equals(COMMIT) || call.name.equals(COMMIT_ALLOWING_LOSS)) - && call.owner.equals(mRecycleOwner)) { - if (values != null && values.size() == 1 && values.get(0) == INSTANCE) { - mIsCleanedUp = true; - return INSTANCE; - } - } else if (call.owner.equals(mRecycleOwner) - && hasReturnType(mRecycleOwner, call.desc)) { - // Called method which returns self. This helps handle cases where you call - // createTransaction().method1().method2().method3().commit() -- if - // method1, 2 and 3 all return "this" then the commit call is really - // called on the createTransaction instance - return INSTANCE; - } - } - } - - if (values != null && values.size() >= 1) { - // Skip the first element: method calls *on* the TypedArray are okay - int start = node.getOpcode() == Opcodes.INVOKESTATIC ? 0 : 1; - for (int i = 0, n = values.size(); i < n; i++) { - Object v = values.get(i); - if (v == INSTANCE && i >= start) { - // Known special cases - if (node.getOpcode() == Opcodes.INVOKESTATIC) { - assert call != null; - if (call.name.equals(OBTAIN) && - call.owner.equals(MOTION_EVENT_CLS)) { - return UNKNOWN; - } - } - - // Passing the instance to another method: could leak - // the instance out of this method (for example calling - // a method which recycles it on our behalf, or store it - // in some holder which will recycle it later). In this - // case, just assume that things are okay. - mEscapes = true; - } else if (v == RECYCLED && call != null) { - Location location = mContext.getLocation(call); - String message = String.format("This %1$s has already been recycled", - mRecycleOwner.substring(mRecycleOwner.lastIndexOf('/') + 1)); - mContext.report(RECYCLE_RESOURCE, mMethod, call, location, message, null); - } - } - } - - return UNKNOWN; - } - - @Override - public Value unaryOperation(AbstractInsnNode node, Value value) throws AnalyzerException { - return value; - } - - @Override - public Value ternaryOperation(AbstractInsnNode node, Value value1, Value value2, - Value value3) throws AnalyzerException { - if (value1 == RECYCLED || value2 == RECYCLED || value3 == RECYCLED) { - return RECYCLED; - } else if (value1 == INSTANCE || value2 == INSTANCE || value3 == INSTANCE) { - return INSTANCE; - } - return UNKNOWN; - } - - @Override - public void returnOperation(AbstractInsnNode node, Value value1, Value value2) - throws AnalyzerException { - if (value1 == INSTANCE || value2 == INSTANCE) { - mEscapes = true; - } - } - - @Override - public Value merge(Value value1, Value value2) { - if (value1 == RECYCLED || value2 == RECYCLED) { - return RECYCLED; - } else if (value1 == INSTANCE || value2 == INSTANCE) { - return INSTANCE; - } - return UNKNOWN; - } - } - - private static class ResourceAnalyzer extends Analyzer { - private Frame mCurrent; - private Frame mFrame1; - private Frame mFrame2; - - public ResourceAnalyzer(Interpreter interpreter) { - super(interpreter); - } - - Frame getCurrentFrame() { - return mCurrent; - } - - @Override - protected void init(String owner, MethodNode m) throws AnalyzerException { - mCurrent = mFrame2; - super.init(owner, m); - } - - @Override - protected Frame newFrame(int nLocals, int nStack) { - // Stash the two most recent frame allocations. When init is called the second - // most recently seen frame is the current frame used during execution, which - // is where we need to replace INSTANCE with RECYCLED when the void - // recycle method is called. - Frame newFrame = super.newFrame(nLocals, nStack); - mFrame2 = mFrame1; - mFrame1 = newFrame; - return newFrame; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ColorUsageDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ColorUsageDetector.java deleted file mode 100644 index e7ffcb6..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ColorUsageDetector.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.RESOURCE_CLZ_COLOR; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -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 java.io.File; - -import lombok.ast.AstVisitor; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.Select; - -/** - * Looks for cases where the code attempts to set a resource id, rather than - * a resolved color, as the RGB int. - */ -public class ColorUsageDetector extends Detector implements Detector.JavaScanner { - /** Attempting to set a resource id as a color */ - public static final Issue ISSUE = Issue.create( - "ResourceAsColor", //$NON-NLS-1$ - "Looks for calls to setColor where a resource id is passed instead of a " + - "resolved color", - - "Methods that take a color in the form of an integer should be passed " + - "an RGB triple, not the actual color resource id. You must call " + - "`getResources().getColor(resource)` to resolve the actual color value first.", - - Category.CORRECTNESS, - 7, - Severity.ERROR, - ColorUsageDetector.class, - Scope.JAVA_FILE_SCOPE); - - /** Constructs a new {@link ColorUsageDetector} check */ - public ColorUsageDetector() { - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements JavaScanner ---- - - @Override - public boolean appliesToResourceRefs() { - return true; - } - - @Override - public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull Node select, @NonNull String type, @NonNull String name, boolean isFramework) { - if (type.equals(RESOURCE_CLZ_COLOR)) { - while (select.getParent() instanceof Select) { - select = select.getParent(); - } - - // See if this method is being called on a setter - if (select.getParent() instanceof MethodInvocation) { - MethodInvocation call = (MethodInvocation) select.getParent(); - String methodName = call.astName().astValue(); - if (methodName.endsWith("Color") //$NON-NLS-1$ - && methodName.startsWith("set")) { //$NON-NLS-1$ - context.report( - ISSUE, select, context.getLocation(select), String.format( - "Should pass resolved color instead of resource id here: " + - "getResources().getColor(%1$s)", select.toString()), - null); - } - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java deleted file mode 100644 index 6f07e77..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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 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.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -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 java.io.File; -import java.util.Collections; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.Comment; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Node; - -/** - * Looks for issues in Java comments - */ -public class CommentDetector extends Detector implements Detector.JavaScanner { - private static final String STOPSHIP_COMMENT = "STOPSHIP"; //$NON-NLS-1$ - - /** Looks for hidden code */ - public static final Issue EASTEREGG = Issue.create( - "EasterEgg", //$NON-NLS-1$ - "Looks for hidden easter eggs", - "An \"easter egg\" is code deliberately hidden in the code, both from potential " + - "users and even from other developers. This lint check looks for code which " + - "looks like it may be hidden from sight.", - Category.SECURITY, - 6, - Severity.WARNING, - CommentDetector.class, - Scope.JAVA_FILE_SCOPE).setEnabledByDefault(false); - - /** Looks for special comment markers intended to stop shipping the code */ - public static final Issue STOPSHIP = Issue.create( - "StopShip", //$NON-NLS-1$ - "Looks for comment markers of the form \"STOPSHIP\" which indicates that code " + - "should not be released yet", - - "Using the comment `// STOPSHIP` can be used to flag code that is incomplete but " + - "checked in. This comment marker can be used to indicate that the code should not " + - "be shipped until the issue is addressed, and lint will look for these.", - Category.CORRECTNESS, - 10, - Severity.WARNING, - CommentDetector.class, - Scope.JAVA_FILE_SCOPE).setEnabledByDefault(false); - - private static final String ESCAPE_STRING = "\\u002a\\u002f"; //$NON-NLS-1$ - - /** Lombok's AST only passes comment nodes for Javadoc so I need to do manual token scanning - instead */ - private static final boolean USE_AST = false; - - - /** Constructs a new {@link CommentDetector} check */ - public CommentDetector() { - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.NORMAL; - } - - @Override - public List<Class<? extends Node>> getApplicableNodeTypes() { - if (USE_AST) { - return Collections.<Class<? extends Node>>singletonList(Comment.class); - } else { - return null; - } - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - // Lombok does not generate comment nodes for block and line comments, only for - // javadoc comments! - if (USE_AST) { - return new CommentChecker(context); - } else { - String source = context.getContents(); - if (source == null) { - return null; - } - // Process the Java source such that we pass tokens to it - - for (int i = 0, n = source.length() - 1; i < n; i++) { - char c = source.charAt(i); - if (c == '\\') { - i += 1; - } else if (c == '/') { - char next = source.charAt(i + 1); - if (next == '/') { - // Line comment - int start = i + 2; - int end = source.indexOf('\n', start); - if (end == -1) { - end = n; - } - checkComment(context, source, 0, start, end); - } else if (next == '*') { - // Block comment - int start = i + 2; - int end = source.indexOf("*/", start); - if (end == -1) { - end = n; - } - checkComment(context, source, 0, start, end); - } - } - } - return null; - } - } - - private static class CommentChecker extends ForwardingAstVisitor { - private final JavaContext mContext; - - public CommentChecker(JavaContext context) { - mContext = context; - } - - @Override - public boolean visitComment(Comment node) { - String contents = node.astContent(); - checkComment(mContext, contents, node.getPosition().getStart(), 0, contents.length()); - return super.visitComment(node); - } - } - - private static void checkComment( - @NonNull Context context, - @NonNull String source, - int offset, - int start, - int end) { - char prev = 0; - char c; - for (int i = start; i < end - 2; i++, prev = c) { - c = source.charAt(i); - if (prev == '\\') { - if (c == 'u' || c == 'U') { - if (source.regionMatches(true, i - 1, ESCAPE_STRING, - 0, ESCAPE_STRING.length())) { - Location location = Location.create(context.file, source, - offset + i - 1, offset + i - 1 + ESCAPE_STRING.length()); - context.report(EASTEREGG, location, - "Code might be hidden here; found unicode escape sequence " + - "which is interpreted as comment end, compiled code follows", - null); - } - } else { - i++; - } - } else if (prev == 'S' && c == 'T' && - source.regionMatches(i - 1, STOPSHIP_COMMENT, 0, STOPSHIP_COMMENT.length())) { - // TODO: Only flag this issue in release mode?? - Location location = Location.create(context.file, source, - offset + i - 1, offset + i - 1 + STOPSHIP_COMMENT.length()); - context.report(STOPSHIP, location, - "STOPSHIP comment found; points to code which must be fixed prior " + - "to release", - null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ControlFlowGraph.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ControlFlowGraph.java deleted file mode 100644 index cbafe28..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ControlFlowGraph.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.google.common.collect.Maps; - -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FrameNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.LabelNode; -import org.objectweb.asm.tree.LineNumberNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.TryCatchBlockNode; -import org.objectweb.asm.tree.analysis.Analyzer; -import org.objectweb.asm.tree.analysis.AnalyzerException; -import org.objectweb.asm.tree.analysis.BasicInterpreter; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * A {@linkplain ControlFlowGraph} is a graph containing a node for each - * instruction in a method, and an edge for each possible control flow; usually - * just "next" for the instruction following the current instruction, but in the - * case of a branch such as an "if", multiple edges to each successive location, - * or with a "goto", a single edge to the jumped-to instruction. - * <p> - * It also adds edges for abnormal control flow, such as the possibility of a - * method call throwing a runtime exception. - */ -public class ControlFlowGraph { - /** Map from instructions to nodes */ - private Map<AbstractInsnNode, Node> mNodeMap; - - /** - * Creates a new {@link ControlFlowGraph} and populates it with the flow - * control for the given method. If the optional {@code initial} parameter is - * provided with an existing graph, then the graph is simply populated, not - * created. This allows subclassing of the graph instance, if necessary. - * - * @param initial usually null, but can point to an existing instance of a - * {@link ControlFlowGraph} in which that graph is reused (but - * populated with new edges) - * @param classNode the class containing the method to be analyzed - * @param method the method to be analyzed - * @return a {@link ControlFlowGraph} with nodes for the control flow in the - * given method - * @throws AnalyzerException if the underlying bytecode library is unable to - * analyze the method bytecode - */ - @NonNull - public static ControlFlowGraph create( - @Nullable ControlFlowGraph initial, - @NonNull ClassNode classNode, - @NonNull MethodNode method) throws AnalyzerException { - final ControlFlowGraph graph = initial != null ? initial : new ControlFlowGraph(); - final InsnList instructions = method.instructions; - graph.mNodeMap = Maps.newHashMapWithExpectedSize(instructions.size()); - - // Create a flow control graph using ASM4's analyzer. According to the ASM 4 guide - // (download.forge.objectweb.org/asm/asm4-guide.pdf) there are faster ways to construct - // it, but those require a lot more code. - Analyzer analyzer = new Analyzer(new BasicInterpreter()) { - @Override - protected void newControlFlowEdge(int insn, int successor) { - // Update the information as of whether the this object has been - // initialized at the given instruction. - AbstractInsnNode from = instructions.get(insn); - AbstractInsnNode to = instructions.get(successor); - graph.add(from, to); - } - - @Override - protected boolean newControlFlowExceptionEdge(int insn, TryCatchBlockNode tcb) { - AbstractInsnNode from = instructions.get(insn); - graph.exception(from, tcb); - return super.newControlFlowExceptionEdge(insn, tcb); - } - - @Override - protected boolean newControlFlowExceptionEdge(int insn, int successor) { - AbstractInsnNode from = instructions.get(insn); - AbstractInsnNode to = instructions.get(successor); - graph.exception(from, to); - return super.newControlFlowExceptionEdge(insn, successor); - } - }; - - analyzer.analyze(classNode.name, method); - return graph; - } - - /** A {@link Node} is a node in the control flow graph for a method, pointing to - * the instruction and its possible successors */ - public static class Node { - /** The instruction */ - public final AbstractInsnNode instruction; - /** Any normal successors (e.g. following instruction, or goto or conditional flow) */ - public final List<Node> successors = new ArrayList<Node>(2); - /** Any abnormal successors (e.g. the handler to go to following an exception) */ - public final List<Node> exceptions = new ArrayList<Node>(1); - - /** A tag for use during depth-first-search iteration of the graph etc */ - public int visit; - - /** - * Constructs a new control graph node - * - * @param instruction the instruction to associate with this node - */ - public Node(@NonNull AbstractInsnNode instruction) { - this.instruction = instruction; - } - - void addSuccessor(@NonNull Node node) { - if (!successors.contains(node)) { - successors.add(node); - } - } - - void addExceptionPath(@NonNull Node node) { - if (!exceptions.contains(node)) { - exceptions.add(node); - } - } - - /** - * Represents this instruction as a string, for debugging purposes - * - * @param includeAdjacent whether it should include a display of - * adjacent nodes as well - * @return a string representation - */ - @NonNull - public String toString(boolean includeAdjacent) { - StringBuilder sb = new StringBuilder(100); - - sb.append(getId(instruction)); - sb.append(':'); - - if (instruction instanceof LabelNode) { - //LabelNode l = (LabelNode) instruction; - //sb.append('L' + l.getLabel().getOffset() + ":"); - //sb.append('L' + l.getLabel().info + ":"); - sb.append("LABEL"); - } else if (instruction instanceof LineNumberNode) { - sb.append("LINENUMBER ").append(((LineNumberNode)instruction).line); - } else if (instruction instanceof FrameNode) { - sb.append("FRAME"); - } else { - int opcode = instruction.getOpcode(); - // AbstractVisitor isn't available unless debug/util is included, - boolean printed = false; - try { - Class<?> cls = Class.forName("org.objectweb.asm.util"); //$NON-NLS-1$ - Field field = cls.getField("OPCODES"); - String[] OPCODES = (String[]) field.get(null); - printed = true; - if (opcode > 0 && opcode <= OPCODES.length) { - sb.append(OPCODES[opcode]); - if (instruction.getType() == AbstractInsnNode.METHOD_INSN) { - sb.append('(').append(((MethodInsnNode)instruction).name).append(')'); - } - } - } catch (Throwable t) { - // debug not installed: just do toString() on the instructions - } - if (!printed) { - if (instruction.getType() == AbstractInsnNode.METHOD_INSN) { - sb.append('(').append(((MethodInsnNode)instruction).name).append(')'); - } else { - sb.append(instruction.toString()); - } - } - } - - if (includeAdjacent) { - if (successors != null && !successors.isEmpty()) { - sb.append(" Next:"); - for (Node successor : successors) { - sb.append(' '); - sb.append(successor.toString(false)); - } - } - - if (exceptions != null && !exceptions.isEmpty()) { - sb.append(" Exceptions:"); - for (Node exception : exceptions) { - sb.append(' '); - sb.append(exception.toString(false)); - } - } - sb.append('\n'); - } - - return sb.toString(); - } - } - - /** Adds an exception flow to this graph */ - protected void add(@NonNull AbstractInsnNode from, @NonNull AbstractInsnNode to) { - getNode(from).addSuccessor(getNode(to)); - } - - /** Adds an exception flow to this graph */ - protected void exception(@NonNull AbstractInsnNode from, @NonNull AbstractInsnNode to) { - // For now, these edges appear useless; we also get more specific - // information via the TryCatchBlockNode which we use instead. - //getNode(from).addExceptionPath(getNode(to)); - } - - /** Adds an exception try block node to this graph */ - protected void exception(@NonNull AbstractInsnNode from, @NonNull TryCatchBlockNode tcb) { - // Add tcb's to all instructions in the range - LabelNode start = tcb.start; - LabelNode end = tcb.end; // exclusive - - // Add exception edges for all method calls in the range - AbstractInsnNode curr = start; - Node handlerNode = getNode(tcb.handler); - while (curr != end && curr != null) { - if (curr.getType() == AbstractInsnNode.METHOD_INSN) { - // Method call; add exception edge to handler - if (tcb.type == null) { - // finally block: not an exception path - getNode(curr).addSuccessor(handlerNode); - } - getNode(curr).addExceptionPath(handlerNode); - } - curr = curr.getNext(); - } - } - - /** - * Looks up (and if necessary) creates a graph node for the given instruction - * - * @param instruction the instruction - * @return the control flow graph node corresponding to the given - * instruction - */ - @NonNull - public Node getNode(@NonNull AbstractInsnNode instruction) { - Node node = mNodeMap.get(instruction); - if (node == null) { - node = new Node(instruction); - mNodeMap.put(instruction, node); - } - - return node; - } - - /** - * Creates a human readable version of the graph - * - * @param start the starting instruction, or null if not known or to use the - * first instruction - * @return a string version of the graph - */ - @NonNull - public String toString(@Nullable Node start) { - StringBuilder sb = new StringBuilder(400); - - AbstractInsnNode curr; - if (start != null) { - curr = start.instruction; - } else { - if (mNodeMap.isEmpty()) { - return "<empty>"; - } else { - curr = mNodeMap.keySet().iterator().next(); - while (curr.getPrevious() != null) { - curr = curr.getPrevious(); - } - } - } - - while (curr != null) { - Node node = mNodeMap.get(curr); - if (node != null) { - sb.append(node.toString(true)); - } - curr = curr.getNext(); - } - - return sb.toString(); - } - - @Override - public String toString() { - return toString(null); - } - - // ---- For debugging only ---- - - private static Map<Object, String> sIds = null; - private static int sNextId = 1; - private static String getId(Object object) { - if (sIds == null) { - sIds = Maps.newHashMap(); - } - String id = sIds.get(object); - if (id == null) { - id = Integer.toString(sNextId++); - sIds.put(object, id); - } - return id; - } -} - diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/CutPasteDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/CutPasteDetector.java deleted file mode 100644 index 84fb6b6..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/CutPasteDetector.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.RESOURCE_CLZ_ID; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -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.google.common.collect.Maps; - -import java.io.File; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import lombok.ast.ArrayAccess; -import lombok.ast.AstVisitor; -import lombok.ast.BinaryExpression; -import lombok.ast.Cast; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.If; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.Select; -import lombok.ast.Statement; -import lombok.ast.VariableDefinitionEntry; -import lombok.ast.VariableReference; - -/** - * Detector looking for cut & paste issues - */ -public class CutPasteDetector extends Detector implements Detector.JavaScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "CutPasteId", //$NON-NLS-1$ - "Looks for code cut & paste mistakes in findViewById() calls", - - "This lint check looks for cases where you have cut & pasted calls to " + - "`findViewById` but have forgotten to update the R.id field. It's possible " + - "that your code is simply (redundantly) looking up the field repeatedly, " + - "but lint cannot distinguish that from a case where you for example want to " + - "initialize fields `prev` and `next` and you cut & pasted `findViewById(R.id.prev)` " + - "and forgot to update the second initialization to `R.id.next`.", - - Category.CORRECTNESS, - 6, - Severity.WARNING, - CutPasteDetector.class, - Scope.JAVA_FILE_SCOPE); - - private Node mLastMethod; - private Map<String, MethodInvocation> mIds; - private Map<String, String> mLhs; - private Map<String, String> mCallOperands; - - /** Constructs a new {@link CutPasteDetector} check */ - public CutPasteDetector() { - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - // ---- Implements JavaScanner ---- - - @Override - public List<String> getApplicableMethodNames() { - return Collections.singletonList("findViewById"); //$NON-NLS-1$ - } - - @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation call) { - String lhs = getLhs(call); - if (lhs == null) { - return; - } - - Node method = JavaContext.findSurroundingMethod(call); - if (method == null) { - return; - } else if (method != mLastMethod) { - mIds = Maps.newHashMap(); - mLhs = Maps.newHashMap(); - mCallOperands = Maps.newHashMap(); - mLastMethod = method; - } - - String callOperand = call.astOperand() != null ? call.astOperand().toString() : ""; - - Expression first = call.astArguments().first(); - if (first instanceof Select) { - Select select = (Select) first; - String id = select.astIdentifier().astValue(); - Expression operand = select.astOperand(); - if (operand instanceof Select) { - Select type = (Select) operand; - if (type.astIdentifier().astValue().equals(RESOURCE_CLZ_ID)) { - if (mIds.containsKey(id)) { - if (lhs.equals(mLhs.get(id))) { - return; - } - if (!callOperand.equals(mCallOperands.get(id))) { - return; - } - MethodInvocation earlierCall = mIds.get(id); - if (!isReachableFrom(method, earlierCall, call)) { - return; - } - Location location = context.getLocation(call); - Location secondary = context.getLocation(earlierCall); - secondary.setMessage("First usage here"); - location.setSecondary(secondary); - context.report(ISSUE, call, location, String.format( - "The id %1$s has already been looked up in this method; possible " + - "cut & paste error?", first.toString()), null); - } else { - mIds.put(id, call); - mLhs.put(id, lhs); - mCallOperands.put(id, callOperand); - } - } - } - } - } - - @Nullable - private static String getLhs(@NonNull MethodInvocation call) { - Node parent = call.getParent(); - if (parent instanceof Cast) { - parent = parent.getParent(); - } - - if (parent instanceof VariableDefinitionEntry) { - VariableDefinitionEntry vde = (VariableDefinitionEntry) parent; - return vde.astName().astValue(); - } else if (parent instanceof BinaryExpression) { - BinaryExpression be = (BinaryExpression) parent; - Expression left = be.astLeft(); - if (left instanceof VariableReference || left instanceof Select) { - return be.astLeft().toString(); - } else if (left instanceof ArrayAccess) { - ArrayAccess aa = (ArrayAccess) left; - return aa.astOperand().toString(); - } - } - - return null; - } - - private static boolean isReachableFrom( - @NonNull Node method, - @NonNull MethodInvocation from, - @NonNull MethodInvocation to) { - ReachableVisitor visitor = new ReachableVisitor(from, to); - method.accept(visitor); - - return visitor.isReachable(); - } - - private static class ReachableVisitor extends ForwardingAstVisitor { - @NonNull private final MethodInvocation mFrom; - @NonNull private final MethodInvocation mTo; - private boolean mReachable; - private boolean mSeenEnd; - - public ReachableVisitor(@NonNull MethodInvocation from, @NonNull MethodInvocation to) { - mFrom = from; - mTo = to; - } - - boolean isReachable() { - return mReachable; - } - - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (node == mFrom) { - mReachable = true; - } else if (node == mTo) { - mSeenEnd = true; - - } - return super.visitMethodInvocation(node); - } - - @Override - public boolean visitIf(If node) { - Expression condition = node.astCondition(); - Statement body = node.astStatement(); - Statement elseBody = node.astElseStatement(); - if (condition != null) { - condition.accept(this); - } - if (body != null) { - boolean wasReachable = mReachable; - body.accept(this); - mReachable = wasReachable; - } - if (elseBody != null) { - boolean wasReachable = mReachable; - elseBody.accept(this); - mReachable = wasReachable; - } - - endVisit(node); - - return false; - } - - @Override - public boolean visitNode(Node node) { - return mSeenEnd; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DeprecationDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DeprecationDetector.java deleted file mode 100644 index 6475a7a..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DeprecationDetector.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ABSOLUTE_LAYOUT; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_AUTO_TEXT; -import static com.android.SdkConstants.ATTR_CAPITALIZE; -import static com.android.SdkConstants.ATTR_EDITABLE; -import static com.android.SdkConstants.ATTR_ENABLED; -import static com.android.SdkConstants.ATTR_INPUT_METHOD; -import static com.android.SdkConstants.ATTR_NUMERIC; -import static com.android.SdkConstants.ATTR_PASSWORD; -import static com.android.SdkConstants.ATTR_PHONE_NUMBER; -import static com.android.SdkConstants.ATTR_SINGLE_LINE; -import static com.android.SdkConstants.EDIT_TEXT; -import static com.android.SdkConstants.VALUE_TRUE; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -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.Attr; -import org.w3c.dom.Element; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -/** - * Check which looks for usage of deprecated tags, attributes, etc. - */ -public class DeprecationDetector extends LayoutDetector { - /** Usage of deprecated views or attributes */ - public static final Issue ISSUE = Issue.create( - "Deprecated", //$NON-NLS-1$ - "Looks for usages of deprecated layouts, attributes, and so on.", - "Deprecated views, attributes and so on are deprecated because there " + - "is a better way to do something. Do it that new way. You've been warned.", - Category.CORRECTNESS, - 2, - Severity.WARNING, - DeprecationDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new {@link DeprecationDetector} */ - public DeprecationDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList( - ABSOLUTE_LAYOUT - ); - } - - @Override - public Collection<String> getApplicableAttributes() { - return Arrays.asList( - // TODO: fill_parent is deprecated as of API 8. - // We could warn about it, but it will probably be very noisy - // and make people disable the deprecation check; let's focus on - // some older flags for now - //"fill_parent", - - ATTR_EDITABLE, - ATTR_INPUT_METHOD, - ATTR_AUTO_TEXT, - ATTR_CAPITALIZE, - - // This flag is still used a lot and is still properly handled by TextView - // so in the interest of not being too noisy and make people ignore all the - // output, keep quiet about this one -for now-. - //ATTR_SINGLE_LINE, - - // This attribute is marked deprecated in android.R.attr but apparently - // using the suggested replacement of state_enabled doesn't work, see issue 27613 - //ATTR_ENABLED, - - ATTR_NUMERIC, - ATTR_PHONE_NUMBER, - ATTR_PASSWORD - - // These attributes are also deprecated; not yet enabled until we - // know the API level to apply the deprecation for: - - // "ignored as of ICS (but deprecated earlier)" - //"fadingEdge", - - // "This attribute is not used by the Android operating system." - //"restoreNeedsApplication", - - // "This will create a non-standard UI appearance, because the search bar UI is - // changing to use only icons for its buttons." - //"searchButtonText", - - ); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - context.report(ISSUE, element, context.getLocation(element), - String.format("%1$s is deprecated", element.getTagName()), null); - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - if (!ANDROID_URI.equals(attribute.getNamespaceURI())) { - return; - } - - String name = attribute.getLocalName(); - String fix; - int minSdk = 1; - if (name.equals(ATTR_EDITABLE)) { - if (!EDIT_TEXT.equals(attribute.getOwnerElement().getTagName())) { - fix = "Use an <EditText> to make it editable"; - } else { - if (VALUE_TRUE.equals(attribute.getValue())) { - fix = "<EditText> is already editable"; - } else { - fix = "Use inputType instead"; - } - } - } else if (name.equals(ATTR_ENABLED)) { - fix = "Use state_enabled instead"; - } else if (name.equals(ATTR_SINGLE_LINE)) { - fix = "Use maxLines=\"1\" instead"; - } else { - assert name.equals(ATTR_INPUT_METHOD) - || name.equals(ATTR_CAPITALIZE) - || name.equals(ATTR_NUMERIC) - || name.equals(ATTR_PHONE_NUMBER) - || name.equals(ATTR_PASSWORD) - || name.equals(ATTR_AUTO_TEXT); - fix = "Use inputType instead"; - // The inputType attribute was introduced in API 3 so don't warn about - // deprecation if targeting older platforms - minSdk = 3; - } - - if (context.getProject().getMinSdk() < minSdk) { - return; - } - - context.report(ISSUE, attribute, context.getLocation(attribute), - String.format("%1$s is deprecated: %2$s", - attribute.getName(), fix), null); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java deleted file mode 100644 index 2b24732..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_PKG_PREFIX; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_CLASS; -import static com.android.SdkConstants.ATTR_CORE_APP; -import static com.android.SdkConstants.ATTR_LAYOUT; -import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_PACKAGE; -import static com.android.SdkConstants.ATTR_STYLE; -import static com.android.SdkConstants.TOOLS_URI; -import static com.android.SdkConstants.VIEW_TAG; -import static com.android.SdkConstants.XMLNS_PREFIX; -import static com.android.resources.ResourceFolderType.ANIM; -import static com.android.resources.ResourceFolderType.ANIMATOR; -import static com.android.resources.ResourceFolderType.COLOR; -import static com.android.resources.ResourceFolderType.DRAWABLE; -import static com.android.resources.ResourceFolderType.INTERPOLATOR; -import static com.android.resources.ResourceFolderType.LAYOUT; -import static com.android.resources.ResourceFolderType.MENU; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -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.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.util.Collection; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Set; - -/** - * Detects layout attributes on builtin Android widgets that do not specify - * a prefix but probably should. - */ -public class DetectMissingPrefix extends LayoutDetector { - - /** Attributes missing the android: prefix */ - public static final Issue MISSING_NAMESPACE = Issue.create( - "MissingPrefix", //$NON-NLS-1$ - "Detect XML attributes not using the Android namespace", - "Most Android views have attributes in the Android namespace. When referencing " + - "these attributes you *must* include the namespace prefix, or your attribute will " + - "be interpreted by `aapt` as just a custom attribute.\n" + - "\n" + - "Similarly, in manifest files, nearly all attributes should be in the `android:` " + - "namespace.", - - Category.CORRECTNESS, - 6, - Severity.ERROR, - DetectMissingPrefix.class, - EnumSet.of(Scope.MANIFEST, Scope.RESOURCE_FILE)) - .addAnalysisScope(Scope.MANIFEST_SCOPE) - .addAnalysisScope(Scope.RESOURCE_FILE_SCOPE); - - private static final Set<String> NO_PREFIX_ATTRS = new HashSet<String>(); - static { - NO_PREFIX_ATTRS.add(ATTR_CLASS); - NO_PREFIX_ATTRS.add(ATTR_STYLE); - NO_PREFIX_ATTRS.add(ATTR_LAYOUT); - NO_PREFIX_ATTRS.add(ATTR_PACKAGE); - NO_PREFIX_ATTRS.add(ATTR_CORE_APP); - } - - /** Constructs a new {@link DetectMissingPrefix} */ - public DetectMissingPrefix() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == LAYOUT - || folderType == MENU - || folderType == DRAWABLE - || folderType == ANIM - || folderType == ANIMATOR - || folderType == COLOR - || folderType == INTERPOLATOR; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableAttributes() { - return ALL; - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - String uri = attribute.getNamespaceURI(); - if (uri == null || uri.isEmpty()) { - String name = attribute.getName(); - if (name == null) { - return; - } - if (NO_PREFIX_ATTRS.contains(name)) { - return; - } - - Element element = attribute.getOwnerElement(); - if (isCustomView(element) && context.getResourceFolderType() != null) { - return; - } - - if (name.startsWith(XMLNS_PREFIX)) { - return; - } - - context.report(MISSING_NAMESPACE, attribute, - context.getLocation(attribute), - "Attribute is missing the Android namespace prefix", - null); - } else if (!ANDROID_URI.equals(uri) - && !TOOLS_URI.equals(uri) - && context.getResourceFolderType() == ResourceFolderType.LAYOUT - && !isCustomView(attribute.getOwnerElement()) - && !attribute.getLocalName().startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) - // TODO: Consider not enforcing that the parent is a custom view - // too, though in that case we should filter out views that are - // layout params for the custom view parent: - // ....&& !attribute.getLocalName().startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) - && attribute.getOwnerElement().getParentNode().getNodeType() == Node.ELEMENT_NODE - && !isCustomView((Element) attribute.getOwnerElement().getParentNode())) { - context.report(MISSING_NAMESPACE, attribute, - context.getLocation(attribute), - String.format("Unexpected namespace prefix \"%1$s\" found for tag %2$s", - attribute.getPrefix(), attribute.getOwnerElement().getTagName()), - null); - } - } - - private static boolean isCustomView(Element element) { - // If this is a custom view, the usage of custom attributes can be legitimate - String tag = element.getTagName(); - if (tag.equals(VIEW_TAG)) { - // <view class="my.custom.view" ...> - return true; - } - - return tag.indexOf('.') != -1 && !tag.startsWith(ANDROID_PKG_PREFIX); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DosLineEndingDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DosLineEndingDetector.java deleted file mode 100644 index 1a2a720..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DosLineEndingDetector.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -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.android.tools.lint.detector.api.XmlContext; - -import org.w3c.dom.Document; - -/** - * Checks that the line endings in DOS files are consistent - */ -public class DosLineEndingDetector extends LayoutDetector { - /** Detects mangled DOS line ending documents */ - public static final Issue ISSUE = Issue.create( - "MangledCRLF", //$NON-NLS-1$ - "Checks that files with DOS line endings are consistent", - - "On Windows, line endings are typically recorded as carriage return plus " + - "newline: \\r\\n.\n" + - "\n" + - "This detector looks for invalid line endings with repeated carriage return " + - "characters (without newlines). Previous versions of the ADT plugin could " + - "accidentally introduce these into the file, and when editing the file, the " + - "editor could produce confusing visual artifacts.", - - Category.CORRECTNESS, - 2, - Severity.ERROR, - DosLineEndingDetector.class, - Scope.RESOURCE_FILE_SCOPE) - .setMoreInfo("https://bugs.eclipse.org/bugs/show_bug.cgi?id=375421"); //$NON-NLS-1$ - - /** Constructs a new {@link DosLineEndingDetector} */ - public DosLineEndingDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.NORMAL; - } - - @Override - public void visitDocument(@NonNull XmlContext context, @NonNull Document document) { - String contents = context.getContents(); - if (contents == null) { - return; - } - - // We could look for *consistency* and complain if you mix \n and \r\n too, - // but that isn't really a problem (most editors handle it) so let's - // not complain needlessly. - - char prev = 0; - for (int i = 0, n = contents.length(); i < n; i++) { - char c = contents.charAt(i); - if (c == '\r' && prev == '\r') { - String message = "Incorrect line ending: found carriage return (\\r) without " + - "corresponding newline (\\n)"; - - // Mark the whole line as the error range, since pointing just to the - // line ending makes the error invisible in IDEs and error reports etc - // Find the most recent non-blank line - boolean blankLine = true; - for (int index = i - 2; index < i; index++) { - char d = contents.charAt(index); - if (!Character.isWhitespace(d)) { - blankLine = false; - } - } - - int lineBegin = i; - for (int index = i - 2; index >= 0; index--) { - char d = contents.charAt(index); - if (d == '\n') { - lineBegin = index + 1; - if (!blankLine) { - break; - } - } else if (!Character.isWhitespace(d)) { - blankLine = false; - } - } - - int lineEnd = Math.min(contents.length(), i + 1); - Location location = Location.create(context.file, contents, lineBegin, lineEnd); - context.report(ISSUE, document.getDocumentElement(), location, message, null); - return; - } - prev = c; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DuplicateIdDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DuplicateIdDetector.java deleted file mode 100644 index de3e4d2..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DuplicateIdDetector.java +++ /dev/null @@ -1,673 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_LAYOUT; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.NEW_ID_PREFIX; -import static com.android.SdkConstants.VIEW_INCLUDE; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -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.android.tools.lint.detector.api.XmlContext; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Checks for duplicate ids within a layout and within an included layout - */ -public class DuplicateIdDetector extends LayoutDetector { - private Set<String> mIds; - private Map<File, Set<String>> mFileToIds; - private Map<File, List<String>> mIncludes; - - // Data structures used for location collection in phase 2 - - // Map from include files to include names to pairs of message and location - // Map from file defining id, to the id to be defined, to a pair of location and message - private Multimap<File, Multimap<String, Occurrence>> mLocations; - private List<Occurrence> mErrors; - - /** The main issue discovered by this detector */ - public static final Issue WITHIN_LAYOUT = Issue.create( - "DuplicateIds", //$NON-NLS-1$ - "Checks for duplicate ids within a single layout", - "Within a layout, id's should be unique since otherwise `findViewById()` can " + - "return an unexpected view.", - Category.CORRECTNESS, - 7, - Severity.WARNING, - DuplicateIdDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** The main issue discovered by this detector */ - public static final Issue CROSS_LAYOUT = Issue.create( - "DuplicateIncludedIds", //$NON-NLS-1$ - "Checks for duplicate ids across layouts that are combined with include tags", - "It's okay for two independent layouts to use the same ids. However, if " + - "layouts are combined with include tags, then the id's need to be unique " + - "within any chain of included layouts, or `Activity#findViewById()` can " + - "return an unexpected view.", - Category.CORRECTNESS, - 6, - Severity.WARNING, - DuplicateIdDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Constructs a duplicate id check */ - public DuplicateIdDetector() { - } - - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.LAYOUT || folderType == ResourceFolderType.MENU; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableAttributes() { - return Collections.singletonList(ATTR_ID); - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList(VIEW_INCLUDE); - } - - @Override - public void beforeCheckFile(@NonNull Context context) { - if (context.getPhase() == 1) { - mIds = new HashSet<String>(); - } - } - - @Override - public void afterCheckFile(@NonNull Context context) { - if (context.getPhase() == 1) { - // Store this layout's set of ids for full project analysis in afterCheckProject - mFileToIds.put(context.file, mIds); - - mIds = null; - } - } - - @Override - public void beforeCheckProject(@NonNull Context context) { - if (context.getPhase() == 1) { - mFileToIds = new HashMap<File, Set<String>>(); - mIncludes = new HashMap<File, List<String>>(); - } - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (context.getPhase() == 1) { - // Look for duplicates - if (!mIncludes.isEmpty()) { - // Traverse all the include chains and ensure that there are no duplicates - // across. - if (context.isEnabled(CROSS_LAYOUT) - && context.getScope().contains(Scope.ALL_RESOURCE_FILES)) { - IncludeGraph graph = new IncludeGraph(context); - graph.check(); - } - } - } else { - assert context.getPhase() == 2; - - if (mErrors != null) { - for (Occurrence occurrence : mErrors) { - //assert location != null : occurrence; - Location location = occurrence.location; - if (location == null) { - location = Location.create(occurrence.file); - } else { - Object clientData = location.getClientData(); - if (clientData instanceof Node) { - Node node = (Node) clientData; - if (context.getDriver().isSuppressed(CROSS_LAYOUT, node)) { - continue; - } - } - } - - List<Occurrence> sorted = new ArrayList<Occurrence>(); - Occurrence curr = occurrence.next; - while (curr != null) { - sorted.add(curr); - curr = curr.next; - } - Collections.sort(sorted); - Location prev = location; - for (Occurrence o : sorted) { - if (o.location != null) { - prev.setSecondary(o.location); - prev = o.location; - } - } - - context.report(CROSS_LAYOUT, location, occurrence.message, null); - } - } - } - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - // Record include graph such that we can look for inter-layout duplicates after the - // project has been fully checked - - String layout = element.getAttribute(ATTR_LAYOUT); // NOTE: Not in android: namespace - if (layout.startsWith(LAYOUT_RESOURCE_PREFIX)) { // Ignore @android:layout/ layouts - layout = layout.substring(LAYOUT_RESOURCE_PREFIX.length()); - - if (context.getPhase() == 1) { - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - List<String> to = mIncludes.get(context.file); - if (to == null) { - to = new ArrayList<String>(); - mIncludes.put(context.file, to); - } - to.add(layout); - } else { - assert context.getPhase() == 2; - - Collection<Multimap<String, Occurrence>> maps = mLocations.get(context.file); - if (maps != null && !maps.isEmpty()) { - for (Multimap<String, Occurrence> map : maps) { - if (!maps.isEmpty()) { - Collection<Occurrence> occurrences = map.get(layout); - if (occurrences != null && !occurrences.isEmpty()) { - for (Occurrence occurrence : occurrences) { - Location location = context.getLocation(element); - location.setClientData(element); - location.setMessage(occurrence.message); - location.setSecondary(occurrence.location); - occurrence.location = location; - } - } - } - } - } - } - } - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - assert attribute.getName().equals(ATTR_ID) || attribute.getLocalName().equals(ATTR_ID); - String id = attribute.getValue(); - if (context.getPhase() == 1) { - if (mIds.contains(id)) { - Location location = context.getLocation(attribute); - - Attr first = findIdAttribute(attribute.getOwnerDocument(), id); - if (first != null && first != attribute) { - Location secondLocation = context.getLocation(first); - secondLocation.setMessage(String.format("%1$s originally defined here", id)); - location.setSecondary(secondLocation); - } - - context.report(WITHIN_LAYOUT, attribute, location, - String.format("Duplicate id %1$s, already defined earlier in this layout", - id), null); - } else if (id.startsWith(NEW_ID_PREFIX)) { - // Skip id's on include tags - if (attribute.getOwnerElement().getTagName().equals(VIEW_INCLUDE)) { - return; - } - - mIds.add(id); - } - } else { - Collection<Multimap<String, Occurrence>> maps = mLocations.get(context.file); - if (maps != null && !maps.isEmpty()) { - for (Multimap<String, Occurrence> map : maps) { - if (!maps.isEmpty()) { - Collection<Occurrence> occurrences = map.get(id); - if (occurrences != null && !occurrences.isEmpty()) { - for (Occurrence occurrence : occurrences) { - if (context.getDriver().isSuppressed(CROSS_LAYOUT, attribute)) { - return; - } - Location location = context.getLocation(attribute); - location.setClientData(attribute); - location.setMessage(occurrence.message); - location.setSecondary(occurrence.location); - occurrence.location = location; - } - } - } - } - } - } - } - - /** Find the first id attribute with the given value below the given node */ - private static Attr findIdAttribute(Node node, String targetValue) { - if (node.getNodeType() == Node.ELEMENT_NODE) { - Attr attribute = ((Element) node).getAttributeNodeNS(ANDROID_URI, ATTR_ID); - if (attribute != null && attribute.getValue().equals(targetValue)) { - return attribute; - } - } - - NodeList children = node.getChildNodes(); - for (int i = 0, n = children.getLength(); i < n; i++) { - Node child = children.item(i); - Attr result = findIdAttribute(child, targetValue); - if (result != null) { - return result; - } - } - - return null; - } - - /** Include Graph Node */ - private static class Layout { - private final File mFile; - private final Set<String> mIds; - private List<Layout> mIncludes; - private List<Layout> mIncludedBy; - - Layout(File file, Set<String> ids) { - mFile = file; - mIds = ids; - } - - Set<String> getIds() { - return mIds; - } - - String getLayoutName() { - return LintUtils.getLayoutName(mFile); - } - - String getDisplayName() { - return mFile.getParentFile().getName() + File.separator + mFile.getName(); - } - - void include(Layout target) { - if (mIncludes == null) { - mIncludes = new ArrayList<Layout>(); - } - mIncludes.add(target); - - if (target.mIncludedBy == null) { - target.mIncludedBy = new ArrayList<Layout>(); - } - target.mIncludedBy.add(this); - } - - boolean isIncluded() { - return mIncludedBy != null && !mIncludedBy.isEmpty(); - } - - File getFile() { - return mFile; - } - - List<Layout> getIncludes() { - return mIncludes; - } - - @Override - public String toString() { - return getDisplayName(); - } - } - - private class IncludeGraph { - private final Context mContext; - private final Map<File, Layout> mFileToLayout; - - public IncludeGraph(Context context) { - mContext = context; - - // Produce a DAG of the files to be included, and compute edges to all eligible - // includes. - // Then visit the DAG and whenever you find a duplicate emit a warning about the - // include path which reached it. - mFileToLayout = new HashMap<File, Layout>(2 * mIncludes.size()); - for (File file : mIncludes.keySet()) { - if (!mFileToLayout.containsKey(file)) { - mFileToLayout.put(file, new Layout(file, mFileToIds.get(file))); - } - } - for (File file : mFileToIds.keySet()) { - Set<String> ids = mFileToIds.get(file); - if (ids != null && !ids.isEmpty()) { - if (!mFileToLayout.containsKey(file)) { - mFileToLayout.put(file, new Layout(file, ids)); - } - } - } - Multimap<String, Layout> nameToLayout = - ArrayListMultimap.create(mFileToLayout.size(), 4); - for (File file : mFileToLayout.keySet()) { - String name = LintUtils.getLayoutName(file); - nameToLayout.put(name, mFileToLayout.get(file)); - } - - // Build up the DAG - for (File file : mIncludes.keySet()) { - Layout from = mFileToLayout.get(file); - assert from != null : file; - - List<String> includedLayouts = mIncludes.get(file); - for (String name : includedLayouts) { - Collection<Layout> layouts = nameToLayout.get(name); - if (layouts != null && !layouts.isEmpty()) { - if (layouts.size() == 1) { - from.include(layouts.iterator().next()); - } else { - // See if we have an obvious match - File folder = from.getFile().getParentFile(); - File candidate = new File(folder, name + DOT_XML); - Layout candidateLayout = mFileToLayout.get(candidate); - if (candidateLayout != null) { - from.include(candidateLayout); - } else if (mFileToIds.containsKey(candidate)) { - // We had an entry in mFileToIds, but not a layout: this - // means that the file exists, but had no includes or ids. - // This can't be a valid match: there is a layout that we know - // the include will pick, but it has no includes (to other layouts) - // and no ids, so no need to look at it - continue; - } else { - for (Layout to : layouts) { - // Decide if the two targets are compatible - if (isCompatible(from, to)) { - from.include(to); - } - } - } - } - } else { - // The layout is including some layout which has no ids or other includes - // so it's not relevant for a duplicate id search - continue; - } - } - } - } - - /** Determine whether two layouts are compatible. They are not if they (for example) - * specify conflicting qualifiers such as {@code -land} and {@code -port}. - * @param from the include from - * @param to the include to - * @return true if the two are compatible */ - boolean isCompatible(Layout from, Layout to) { - File fromFolder = from.mFile.getParentFile(); - File toFolder = to.mFile.getParentFile(); - if (fromFolder.equals(toFolder)) { - return true; - } - - String[] fromQualifiers = fromFolder.getName().split("-"); //$NON-NLS-1$ - String[] toQualifiers = toFolder.getName().split("-"); //$NON-NLS-1$ - - if (isPortrait(fromQualifiers) != isPortrait(toQualifiers)) { - return false; - } - - return true; - } - - private boolean isPortrait(String[] qualifiers) { - for (String qualifier : qualifiers) { - if (qualifier.equals("port")) { //$NON-NLS-1$ - return true; - } else if (qualifier.equals("land")) { //$NON-NLS-1$ - return false; - } - } - - return true; // it's the default - } - - public void check() { - // Visit the DAG, looking for conflicts - for (Layout layout : mFileToLayout.values()) { - if (!layout.isIncluded()) { // Only check from "root" nodes - Deque<Layout> stack = new ArrayDeque<Layout>(); - getIds(layout, stack, new HashSet<Layout>()); - } - } - } - - /** - * Computes the cumulative set of ids used in a given layout. We can't - * just depth-first-search the graph and check the set of ids - * encountered along the way, because we need to detect when multiple - * includes contribute the same ids. For example, if a file is included - * more than once, that would result in duplicates. - */ - private Set<String> getIds(Layout layout, Deque<Layout> stack, Set<Layout> seen) { - seen.add(layout); - - Set<String> layoutIds = layout.getIds(); - List<Layout> includes = layout.getIncludes(); - if (includes != null) { - Set<String> ids = new HashSet<String>(); - if (layoutIds != null) { - ids.addAll(layoutIds); - } - - stack.push(layout); - - Multimap<String, Set<String>> nameToIds = - ArrayListMultimap.create(includes.size(), 4); - - for (Layout included : includes) { - if (seen.contains(included)) { - continue; - } - Set<String> includedIds = getIds(included, stack, seen); - if (includedIds != null) { - String layoutName = included.getLayoutName(); - - idCheck: - for (String id : includedIds) { - if (ids.contains(id)) { - Collection<Set<String>> idSets = nameToIds.get(layoutName); - if (idSets != null) { - for (Set<String> siblingIds : idSets) { - if (siblingIds.contains(id)) { - // The id reference was added by a sibling, - // so no need to complain (again) - continue idCheck; - } - } - } - - // Duplicate! Record location request for new phase. - if (mLocations == null) { - mErrors = new ArrayList<Occurrence>(); - mLocations = ArrayListMultimap.create(); - mContext.getDriver().requestRepeat(DuplicateIdDetector.this, - Scope.ALL_RESOURCES_SCOPE); - } - - Map<Layout, Occurrence> occurrences = - new HashMap<Layout, Occurrence>(); - findId(layout, id, new ArrayDeque<Layout>(), occurrences, - new HashSet<Layout>()); - assert occurrences.size() >= 2; - - // Stash a request to find the given include - Collection<Occurrence> values = occurrences.values(); - List<Occurrence> sorted = new ArrayList<Occurrence>(values); - Collections.sort(sorted); - String msg = String.format( - "Duplicate id %1$s, defined or included multiple " + - "times in %2$s: %3$s", - id, layout.getDisplayName(), - sorted.toString()); - - // Store location request for the <include> tag - Occurrence primary = new Occurrence(layout.getFile(), msg, null); - Multimap<String, Occurrence> m = ArrayListMultimap.create(); - m.put(layoutName, primary); - mLocations.put(layout.getFile(), m); - mErrors.add(primary); - - Occurrence prev = primary; - - // Now store all the included occurrences of the id - for (Occurrence occurrence : values) { - if (occurrence.file.equals(layout.getFile())) { - occurrence.message = "Defined here"; - } else { - occurrence.message = String.format( - "Defined here, included via %1$s", - occurrence.includePath); - } - - m = ArrayListMultimap.create(); - m.put(id, occurrence); - mLocations.put(occurrence.file, m); - - // Link locations together - prev.next = occurrence; - prev = occurrence; - } - } - ids.add(id); - } - - // Store these ids such that on a conflict, we can tell when - // an id was added by a single variation of this file - nameToIds.put(layoutName, includedIds); - } - } - Layout visited = stack.pop(); - assert visited == layout; - return ids; - } else { - return layoutIds; - } - } - - private void findId(Layout layout, String id, Deque<Layout> stack, - Map<Layout, Occurrence> occurrences, Set<Layout> seen) { - seen.add(layout); - - Set<String> layoutIds = layout.getIds(); - if (layoutIds != null && layoutIds.contains(id)) { - StringBuilder path = new StringBuilder(80); - - if (!stack.isEmpty()) { - Iterator<Layout> iterator = stack.descendingIterator(); - while (iterator.hasNext()) { - path.append(iterator.next().getDisplayName()); - path.append(" => "); - } - } - path.append(layout.getDisplayName()); - path.append(" defines "); - path.append(id); - - assert occurrences.get(layout) == null : id + ',' + layout; - occurrences.put(layout, new Occurrence(layout.getFile(), null, path.toString())); - } - - List<Layout> includes = layout.getIncludes(); - if (includes != null) { - stack.push(layout); - for (Layout included : includes) { - if (!seen.contains(included)) { - findId(included, id, stack, occurrences, seen); - } - } - Layout visited = stack.pop(); - assert visited == layout; - } - } - } - - private static class Occurrence implements Comparable<Occurrence> { - public final File file; - public final String includePath; - public Occurrence next; - public Location location; - public String message; - - public Occurrence(File file, String message, String includePath) { - this.file = file; - this.message = message; - this.includePath = includePath; - } - - @Override - public String toString() { - return includePath != null ? includePath : message; - } - - @Override - public int compareTo(Occurrence other) { - // First sort by length, then sort by name - int delta = toString().length() - other.toString().length(); - if (delta != 0) { - return delta; - } - - return toString().compareTo(other.toString()); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DuplicateResourceDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DuplicateResourceDetector.java deleted file mode 100644 index 004303c..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/DuplicateResourceDetector.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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.ATTR_NAME; -import static com.android.SdkConstants.ATTR_TYPE; -import static com.android.SdkConstants.TAG_ITEM; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Location.Handle; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 com.android.utils.Pair; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; - -import java.io.File; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * This detector identifies cases where a resource is defined multiple times in the - * same resource folder - */ -public class DuplicateResourceDetector extends ResourceXmlDetector { - - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "DuplicateDefinition", //$NON-NLS-1$ - "Discovers duplicate definitions of resources", - - "You can define a resource multiple times in different resource folders; that's how " + - "string translations are done, for example. However, defining the same resource " + - "more than once in the same resource folder is likely an error, for example " + - "attempting to add a new resource without realizing that the name is already used, " + - "and so on.", - - Category.CORRECTNESS, - 6, - Severity.ERROR, - DuplicateResourceDetector.class, - Scope.ALL_RESOURCES_SCOPE).addAnalysisScope(Scope.RESOURCE_FILE_SCOPE); - - private static final String PRODUCT = "product"; //$NON-NLS-1$ - private Map<ResourceType, Set<String>> mTypeMap; - private Map<ResourceType, List<Pair<String, Location.Handle>>> mLocations; - private File mParent; - - /** Constructs a new {@link DuplicateResourceDetector} */ - public DuplicateResourceDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.NORMAL; - } - - @Override - @Nullable - public Collection<String> getApplicableAttributes() { - return Collections.singletonList(ATTR_NAME); - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.VALUES; - } - - @Override - public void beforeCheckFile(@NonNull Context context) { - File parent = context.file.getParentFile(); - if (!parent.equals(mParent)) { - mParent = parent; - mTypeMap = Maps.newEnumMap(ResourceType.class); - mLocations = Maps.newEnumMap(ResourceType.class); - } - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - Element element = attribute.getOwnerElement(); - - if (element.hasAttribute(PRODUCT)) { - return; - } - - String tag = element.getTagName(); - String typeString = tag; - if (tag.equals(TAG_ITEM)) { - typeString = element.getAttribute(ATTR_TYPE); - } - ResourceType type = ResourceType.getEnum(typeString); - if (type == null) { - return; - } - - if (type == ResourceType.ATTR - && element.getParentNode().getNodeName().equals( - ResourceType.DECLARE_STYLEABLE.getName())) { - return; - } - - Set<String> names = mTypeMap.get(type); - if (names == null) { - names = Sets.newHashSetWithExpectedSize(40); - mTypeMap.put(type, names); - } - - String name = attribute.getValue(); - if (names.contains(name)) { - String message = String.format("%1$s has already been defined in this folder", - name); - Location location = context.getLocation(attribute); - List<Pair<String, Handle>> list = mLocations.get(type); - for (Pair<String, Handle> pair : list) { - if (name.equals(pair.getFirst())) { - Location secondary = pair.getSecond().resolve(); - secondary.setMessage("Previously defined here"); - location.setSecondary(secondary); - } - } - context.report(ISSUE, attribute, location, message, null); - } else { - names.add(name); - List<Pair<String, Handle>> list = mLocations.get(type); - if (list == null) { - list = Lists.newArrayList(); - mLocations.put(type, list); - } - Location.Handle handle = context.parser.createLocationHandle(context, attribute); - list.add(Pair.of(name, handle)); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ExtraTextDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ExtraTextDetector.java deleted file mode 100644 index e0781db..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ExtraTextDetector.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.DefaultPosition; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Position; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -/** - * Check which looks for invalid resources. Aapt already performs some validation, - * such as making sure that resource references point to resources that exist, but this - * detector looks for additional issues. - */ -public class ExtraTextDetector extends ResourceXmlDetector { - private boolean mFoundText; - - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "ExtraText", //$NON-NLS-1$ - "Looks for extraneous text in layout files", - "Layout resource files should only contain elements and attributes. Any XML " + - "text content found in the file is likely accidental (and potentially " + - "dangerous if the text resembles XML and the developer believes the text " + - "to be functional)", - Category.CORRECTNESS, - 3, - Severity.WARNING, - ExtraTextDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new detector */ - public ExtraTextDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.LAYOUT - || folderType == ResourceFolderType.MENU - || folderType == ResourceFolderType.ANIM - || folderType == ResourceFolderType.ANIMATOR - || folderType == ResourceFolderType.DRAWABLE - || folderType == ResourceFolderType.COLOR; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public void visitDocument(@NonNull XmlContext context, @NonNull Document document) { - mFoundText = false; - visitNode(context, document); - } - - private void visitNode(XmlContext context, Node node) { - short nodeType = node.getNodeType(); - if (nodeType == Node.TEXT_NODE && !mFoundText) { - String text = node.getNodeValue(); - for (int i = 0, n = text.length(); i < n; i++) { - char c = text.charAt(i); - if (!Character.isWhitespace(c)) { - String snippet = text.trim(); - int maxLength = 100; - if (snippet.length() > maxLength) { - snippet = snippet.substring(0, maxLength) + "..."; - } - Location location = context.getLocation(node); - if (i > 0) { - // Adjust the error position to point to the beginning of - // the text rather than the beginning of the text node - // (which is often the newline at the end of the previous - // line and the indentation) - Position start = location.getStart(); - if (start != null) { - int line = start.getLine(); - int column = start.getColumn(); - int offset = start.getOffset(); - - for (int j = 0; j < i; j++) { - offset++; - - if (text.charAt(j) == '\n') { - if (line != -1) { - line++; - } - if (column != -1) { - column = 0; - } - } else if (column != -1) { - column++; - } - } - - start = new DefaultPosition(line, column, offset); - location = Location.create(context.file, start, location.getEnd()); - } - } - context.report(ISSUE, node, location, - String.format("Unexpected text found in layout file: \"%1$s\"", - snippet), null); - mFoundText = true; - break; - } - } - } - - // Visit children - NodeList childNodes = node.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - visitNode(context, child); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/FieldGetterDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/FieldGetterDetector.java deleted file mode 100644 index 04841ab..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/FieldGetterDetector.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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.ClassContext; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -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 org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.VarInsnNode; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Looks for getter calls within the same class that could be replaced by - * direct field references instead. - */ -public class FieldGetterDetector extends Detector implements Detector.ClassScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "FieldGetter", //$NON-NLS-1$ - "Suggests replacing uses of getters with direct field access within a class", - - "Accessing a field within the class that defines a getter for that field is " + - "at least 3 times faster than calling the getter. For simple getters that do " + - "nothing other than return the field, you might want to just reference the " + - "local field directly instead.\n" + - "\n" + - "NOTE: As of Android 2.3 (Gingerbread), this optimization is performed " + - "automatically by Dalvik, so there is no need to change your code; this is " + - "only relevant if you are targeting older versions of Android.", - - Category.PERFORMANCE, - 4, - Severity.WARNING, - FieldGetterDetector.class, - Scope.CLASS_FILE_SCOPE). - // This is a micro-optimization: not enabled by default - setEnabledByDefault(false).setMoreInfo( - "http://developer.android.com/guide/practices/design/performance.html#internal_get_set"); //$NON-NLS-1$ - private ArrayList<Entry> mPendingCalls; - - /** Constructs a new {@link FieldGetterDetector} check */ - public FieldGetterDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements ClassScanner ---- - - @Override - public int[] getApplicableAsmNodeTypes() { - return new int[] { AbstractInsnNode.METHOD_INSN }; - } - - @Override - public void checkInstruction(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull AbstractInsnNode instruction) { - // As of Gingerbread/API 9, Dalvik performs this optimization automatically - if (context.getProject().getMinSdk() >= 9) { - return; - } - - if ((method.access & Opcodes.ACC_STATIC) != 0) { - // Not an instance method - return; - } - - if (instruction.getOpcode() != Opcodes.INVOKEVIRTUAL) { - return; - } - - MethodInsnNode node = (MethodInsnNode) instruction; - String name = node.name; - String owner = node.owner; - - AbstractInsnNode prev = LintUtils.getPrevInstruction(instruction); - if (prev == null || prev.getOpcode() != Opcodes.ALOAD) { - return; - } - VarInsnNode prevVar = (VarInsnNode) prev; - if (prevVar.var != 0) { // Not on "this", variable 0 in instance methods? - return; - } - - if (((name.startsWith("get") && name.length() > 3 //$NON-NLS-1$ - && Character.isUpperCase(name.charAt(3))) - || (name.startsWith("is") && name.length() > 2 //$NON-NLS-1$ - && Character.isUpperCase(name.charAt(2)))) - && owner.equals(classNode.name)) { - // Calling a potential getter method on self. We now need to - // investigate the method body of the getter call and make sure - // it's really a plain getter, not just a method which happens - // to have a method name like a getter, or a method which not - // only returns a field but possibly computes it or performs - // other initialization or side effects. This is done in a - // second pass over the bytecode, initiated by the finish() - // method. - if (mPendingCalls == null) { - mPendingCalls = new ArrayList<Entry>(); - } - - mPendingCalls.add(new Entry(name, node, method)); - } - - super.checkInstruction(context, classNode, method, instruction); - } - - @Override - public void afterCheckFile(@NonNull Context c) { - ClassContext context = (ClassContext) c; - - if (mPendingCalls != null) { - Set<String> names = new HashSet<String>(mPendingCalls.size()); - for (Entry entry : mPendingCalls) { - names.add(entry.name); - } - - Map<String, String> getters = checkMethods(context.getClassNode(), names); - if (!getters.isEmpty()) { - for (String getter : getters.keySet()) { - for (Entry entry : mPendingCalls) { - String name = entry.name; - // There can be more than one reference to the same name: - // one for each call site - if (name.equals(getter)) { - Location location = context.getLocation(entry.call); - String fieldName = getters.get(getter); - if (fieldName == null) { - fieldName = ""; - } - context.report(ISSUE, entry.method, entry.call, location, - String.format( - "Calling getter method %1$s() on self is " + - "slower than field access (%2$s)", getter, fieldName), fieldName); - } - } - } - } - } - - mPendingCalls = null; - } - - // Holder class for getters to be checked - private static class Entry { - public final String name; - public final MethodNode method; - public final MethodInsnNode call; - - public Entry(String name, MethodInsnNode call, MethodNode method) { - super(); - this.name = name; - this.call = call; - this.method = method; - } - } - - // Validate that these getter methods are really just simple field getters - // like these int and String getters: - // public int getFoo(); - // Code: - // 0: aload_0 - // 1: getfield #21; //Field mFoo:I - // 4: ireturn - // - // public java.lang.String getBar(); - // Code: - // 0: aload_0 - // 1: getfield #25; //Field mBar:Ljava/lang/String; - // 4: areturn - // - // Returns a map of valid getters as keys, and if the field name is found, the field name - // for each getter as its value. - private static Map<String, String> checkMethods(ClassNode classNode, Set<String> names) { - Map<String, String> validGetters = Maps.newHashMap(); - @SuppressWarnings("rawtypes") - List methods = classNode.methods; - String fieldName = null; - checkMethod: - for (Object methodObject : methods) { - MethodNode method = (MethodNode) methodObject; - if (names.contains(method.name) - && method.desc.startsWith("()")) { //$NON-NLS-1$ // (): No arguments - InsnList instructions = method.instructions; - int mState = 1; - for (AbstractInsnNode curr = instructions.getFirst(); - curr != null; - curr = curr.getNext()) { - switch (curr.getOpcode()) { - case -1: - // Skip label and line number nodes - continue; - case Opcodes.ALOAD: - if (mState == 1) { - fieldName = null; - mState = 2; - } else { - continue checkMethod; - } - break; - case Opcodes.GETFIELD: - if (mState == 2) { - FieldInsnNode field = (FieldInsnNode) curr; - fieldName = field.name; - mState = 3; - } else { - continue checkMethod; - } - break; - case Opcodes.ARETURN: - case Opcodes.FRETURN: - case Opcodes.IRETURN: - case Opcodes.DRETURN: - case Opcodes.LRETURN: - case Opcodes.RETURN: - if (mState == 3) { - validGetters.put(method.name, fieldName); - } - continue checkMethod; - default: - continue checkMethod; - } - } - } - } - - return validGetters; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/FragmentDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/FragmentDetector.java deleted file mode 100644 index f6ebcd6..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/FragmentDetector.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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 com.android.SdkConstants.FRAGMENT; -import static com.android.SdkConstants.FRAGMENT_V4; - -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.Detector; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -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 org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; - -import java.util.List; - -/** - * Checks that Fragment subclasses can be instantiated via - * {link {@link Class#newInstance()}}: the class is public, static, and has - * a public null constructor. - * <p> - * This helps track down issues like - * http://stackoverflow.com/questions/8058809/fragment-activity-crashes-on-screen-rotate - * (and countless duplicates) - */ -public class FragmentDetector extends Detector implements ClassScanner { - private static final String FRAGMENT_NAME_SUFFIX = "Fragment"; //$NON-NLS-1$ - - /** Are fragment subclasses instantiatable? */ - public static final Issue ISSUE = Issue.create( - "ValidFragment", //$NON-NLS-1$ - "Ensures that Fragment subclasses can be instantiated", - - "From the Fragment documentation:\n" + - "*Every* fragment must have an empty constructor, so it can be instantiated when " + - "restoring its activity's state. It is strongly recommended that subclasses do not " + - "have other constructors with parameters, since these constructors will not be " + - "called when the fragment is re-instantiated; instead, arguments can be supplied " + - "by the caller with `setArguments(Bundle)` and later retrieved by the Fragment " + - "with `getArguments()`.", - - Category.CORRECTNESS, - 6, - Severity.ERROR, - FragmentDetector.class, - Scope.CLASS_FILE_SCOPE).setMoreInfo( - "http://developer.android.com/reference/android/app/Fragment.html#Fragment()"); //$NON-NLS-1$ - - - /** Constructs a new {@link FragmentDetector} */ - public FragmentDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements ClassScanner ---- - - @Override - public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { - if ((classNode.access & Opcodes.ACC_ABSTRACT) != 0) { - // Ignore abstract classes since they are clearly (and by definition) not intended to - // be instantiated. We're looking for accidental non-static or missing constructor - // scenarios here. - return; - } - - LintDriver driver = context.getDriver(); - - if (!(driver.isSubclassOf(classNode, FRAGMENT) - || driver.isSubclassOf(classNode, FRAGMENT_V4))) { - if (!context.getScope().contains(Scope.ALL_JAVA_FILES)) { - // Single file checking: Just check that it looks like a fragment class - // (since we don't have a full superclass map) - if (!classNode.name.endsWith(FRAGMENT_NAME_SUFFIX) || - classNode.superName == null) { - return; - } - } else { - return; - } - } - - if ((classNode.access & Opcodes.ACC_PUBLIC) == 0) { - context.report(ISSUE, context.getLocation(classNode), String.format( - "This fragment class should be public (%1$s)", - ClassContext.createSignature(classNode.name, null, null)), - null); - return; - } - - if (classNode.name.indexOf('$') != -1 && !LintUtils.isStaticInnerClass(classNode)) { - context.report(ISSUE, context.getLocation(classNode), String.format( - "This fragment inner class should be static (%1$s)", - ClassContext.createSignature(classNode.name, null, null)), - null); - return; - } - - boolean hasDefaultConstructor = false; - @SuppressWarnings("rawtypes") // ASM API - List methodList = classNode.methods; - for (Object m : methodList) { - MethodNode method = (MethodNode) m; - if (method.name.equals(CONSTRUCTOR_NAME)) { - if (method.desc.equals("()V")) { //$NON-NLS-1$ - // The constructor must be public - if ((method.access & Opcodes.ACC_PUBLIC) != 0) { - hasDefaultConstructor = true; - } else { - context.report(ISSUE, context.getLocation(method, classNode), - "The default constructor must be public", - null); - // Also mark that we have a constructor so we don't complain again - // below since we've already emitted a more specific error related - // to the default constructor - hasDefaultConstructor = true; - } - } else if (!method.desc.contains("()")) { //$NON-NLS-1$ - context.report(ISSUE, context.getLocation(method, classNode), - // TODO: Use separate issue for this which isn't an error - "Avoid non-default constructors in fragments: use a default constructor " + - "plus Fragment#setArguments(Bundle) instead", - null); - } - } - } - - if (!hasDefaultConstructor) { - context.report(ISSUE, context.getLocation(classNode), String.format( - "This fragment should provide a default constructor (a public " + - "constructor with no arguments) (%1$s)", - ClassContext.createSignature(classNode.name, null, null)), - null); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/GridLayoutDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/GridLayoutDetector.java deleted file mode 100644 index c348502..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/GridLayoutDetector.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_COLUMN_COUNT; -import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN; -import static com.android.SdkConstants.ATTR_LAYOUT_ROW; -import static com.android.SdkConstants.ATTR_ROW_COUNT; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -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.Attr; -import org.w3c.dom.Element; - -import java.util.Collection; -import java.util.Collections; - -/** - * Check which looks for potential errors in declarations of GridLayouts, such as specifying - * row/column numbers outside the declared dimensions of the grid. - */ -public class GridLayoutDetector extends LayoutDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "GridLayout", //$NON-NLS-1$ - "Checks for potential GridLayout errors like declaring rows and columns outside " + - "the declared grid dimensions", - "Declaring a layout_row or layout_column that falls outside the declared size " + - "of a GridLayout's `rowCount` or `columnCount` is usually an unintentional error.", - Category.CORRECTNESS, - 4, - Severity.FATAL, - GridLayoutDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new {@link GridLayoutDetector} check */ - public GridLayoutDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList( - "GridLayout" //$NON-NLS-1$ - ); - } - - private static int getInt(Element element, String attribute, int defaultValue) { - String valueString = element.getAttributeNS(ANDROID_URI, attribute); - if (valueString != null && !valueString.isEmpty()) { - try { - return Integer.decode(valueString); - } catch (NumberFormatException nufe) { - // Ignore - error in user's XML - } - } - - return defaultValue; - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - int declaredRowCount = getInt(element, ATTR_ROW_COUNT, -1); - int declaredColumnCount = getInt(element, ATTR_COLUMN_COUNT, -1); - - if (declaredColumnCount != -1 || declaredRowCount != -1) { - for (Element child : LintUtils.getChildren(element)) { - if (declaredColumnCount != -1) { - int column = getInt(child, ATTR_LAYOUT_COLUMN, -1); - if (column >= declaredColumnCount) { - Attr node = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_COLUMN); - context.report(ISSUE, node, context.getLocation(node), - String.format("Column attribute (%1$d) exceeds declared grid column count (%2$d)", - column, declaredColumnCount), null); - } - } - if (declaredRowCount != -1) { - int row = getInt(child, ATTR_LAYOUT_ROW, -1); - if (row > declaredRowCount) { - Attr node = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_ROW); - context.report(ISSUE, node, context.getLocation(node), - String.format("Row attribute (%1$d) exceeds declared grid row count (%2$d)", - row, declaredRowCount), null); - } - } - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java deleted file mode 100644 index cfe8f0b..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -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 org.objectweb.asm.tree.ClassNode; - -/** - * Checks that Handler implementations are top level classes or static. - * See the corresponding check in the android.os.Handler source code. - */ -public class HandlerDetector extends Detector implements ClassScanner { - - /** Potentially leaking handlers */ - public static final Issue ISSUE = Issue.create( - "HandlerLeak", //$NON-NLS-1$ - "Ensures that Handler classes do not hold on to a reference to an outer class", - - "In Android, Handler classes should be static or leaks might occur. " + - "Messages enqueued on the application thread's MessageQueue also retain their " + - "target Handler. If the Handler is an inner class, its outer class will be " + - "retained as well. To avoid leaking the outer class, declare the Handler as a " + - "static nested class with a WeakReference to its outer class.", - - Category.PERFORMANCE, - 4, - Severity.WARNING, - HandlerDetector.class, - Scope.CLASS_FILE_SCOPE); - - /** Constructs a new {@link HandlerDetector} */ - public HandlerDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements ClassScanner ---- - - @Override - public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { - if (classNode.name.indexOf('$') == -1) { - return; - } - - if (context.getDriver().isSubclassOf(classNode, "android/os/Handler") //$NON-NLS-1$ - && !LintUtils.isStaticInnerClass(classNode)) { - Location location = context.getLocation(classNode); - context.report(ISSUE, location, String.format( - "This Handler class should be static or leaks might occur (%1$s)", - ClassContext.createSignature(classNode.name, null, null)), - null); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/HardcodedDebugModeDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/HardcodedDebugModeDetector.java deleted file mode 100644 index fd678ca..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/HardcodedDebugModeDetector.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_DEBUGGABLE; - -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.Detector; -import com.android.tools.lint.detector.api.Issue; -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.Attr; - -import java.io.File; -import java.util.Collection; -import java.util.Collections; - -/** - * Checks for hardcoded debug mode in manifest files - */ -public class HardcodedDebugModeDetector extends Detector implements Detector.XmlScanner { - - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "HardcodedDebugMode", //$NON-NLS-1$ - "Checks for hardcoded values of android:debuggable in the manifest", - - "It's best to leave out the `android:debuggable` attribute from the manifest. " + - "If you do, then the tools will automatically insert `android:debuggable=true` when " + - "building an APK to debug on an emulator or device. And when you perform a " + - "release build, such as Exporting APK, it will automatically set it to `false`.\n" + - "\n" + - "If on the other hand you specify a specific value in the manifest file, then " + - "the tools will always use it. This can lead to accidentally publishing " + - "your app with debug information.", - - Category.SECURITY, - 5, - Severity.WARNING, - HardcodedDebugModeDetector.class, - Scope.MANIFEST_SCOPE); - - /** Constructs a new {@link HardcodedDebugModeDetector} check */ - public HardcodedDebugModeDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return file.getName().equals(ANDROID_MANIFEST_XML); - } - - // ---- Implements Detector.XmlScanner ---- - - @Override - public Collection<String> getApplicableAttributes() { - return Collections.singleton(ATTR_DEBUGGABLE); - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - if (attribute.getNamespaceURI().equals(ANDROID_URI)) { - //if (attribute.getOwnerElement().getTagName().equals(TAG_APPLICATION)) { - context.report(ISSUE, attribute, context.getLocation(attribute), - "Avoid hardcoding the debug mode; leaving it out allows debug and " + - "release builds to automatically assign one", null); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/HardcodedValuesDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/HardcodedValuesDetector.java deleted file mode 100644 index 11cc19d..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/HardcodedValuesDetector.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_CONTENT_DESCRIPTION; -import static com.android.SdkConstants.ATTR_HINT; -import static com.android.SdkConstants.ATTR_LABEL; -import static com.android.SdkConstants.ATTR_PROMPT; -import static com.android.SdkConstants.ATTR_TEXT; -import static com.android.SdkConstants.ATTR_TITLE; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -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.Attr; - -import java.util.Arrays; -import java.util.Collection; - -/** - * Check which looks at the children of ScrollViews and ensures that they fill/match - * the parent width instead of setting wrap_content. - */ -public class HardcodedValuesDetector extends LayoutDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "HardcodedText", //$NON-NLS-1$ - "Looks for hardcoded text attributes which should be converted to resource lookup", - "Hardcoding text attributes directly in layout files is bad for several reasons:\n" + - "\n" + - "* When creating configuration variations (for example for landscape or portrait)" + - "you have to repeat the actual text (and keep it up to date when making changes)\n" + - "\n" + - "* The application cannot be translated to other languages by just adding new " + - "translations for existing string resources.\n" + - "\n" + - "In Eclipse there is a quickfix to automatically extract this hardcoded string into " + - "a resource lookup.", - - Category.I18N, - 5, - Severity.WARNING, - HardcodedValuesDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - // TODO: Add additional issues here, such as hardcoded colors, hardcoded sizes, etc - - /** Constructs a new {@link HardcodedValuesDetector} */ - public HardcodedValuesDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableAttributes() { - return Arrays.asList( - // Layouts - ATTR_TEXT, - ATTR_CONTENT_DESCRIPTION, - ATTR_HINT, - ATTR_LABEL, - ATTR_PROMPT, - - // Menus - ATTR_TITLE - ); - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.LAYOUT || folderType == ResourceFolderType.MENU; - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - String value = attribute.getValue(); - if (!value.isEmpty() && (value.charAt(0) != '@' && value.charAt(0) != '?')) { - // Make sure this is really one of the android: attributes - if (!ANDROID_URI.equals(attribute.getNamespaceURI())) { - return; - } - - context.report(ISSUE, attribute, context.getLocation(attribute), - String.format("[I18N] Hardcoded string \"%1$s\", should use @string resource", - value), null); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/IconDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/IconDetector.java deleted file mode 100644 index 7f26c96..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/IconDetector.java +++ /dev/null @@ -1,1906 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_ICON; -import static com.android.SdkConstants.DOT_9PNG; -import static com.android.SdkConstants.DOT_GIF; -import static com.android.SdkConstants.DOT_JPEG; -import static com.android.SdkConstants.DOT_JPG; -import static com.android.SdkConstants.DOT_PNG; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.DRAWABLE_FOLDER; -import static com.android.SdkConstants.DRAWABLE_HDPI; -import static com.android.SdkConstants.DRAWABLE_LDPI; -import static com.android.SdkConstants.DRAWABLE_MDPI; -import static com.android.SdkConstants.DRAWABLE_PREFIX; -import static com.android.SdkConstants.DRAWABLE_XHDPI; -import static com.android.SdkConstants.MENU_TYPE; -import static com.android.SdkConstants.R_CLASS; -import static com.android.SdkConstants.R_DRAWABLE_PREFIX; -import static com.android.SdkConstants.TAG_ACTIVITY; -import static com.android.SdkConstants.TAG_APPLICATION; -import static com.android.SdkConstants.TAG_ITEM; -import static com.android.SdkConstants.TAG_PROVIDER; -import static com.android.SdkConstants.TAG_RECEIVER; -import static com.android.SdkConstants.TAG_SERVICE; -import static com.android.tools.lint.detector.api.LintUtils.endsWith; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Project; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; - -import org.w3c.dom.Element; - -import java.awt.Dimension; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.regex.Pattern; - -import javax.imageio.ImageIO; -import javax.imageio.ImageReader; -import javax.imageio.stream.ImageInputStream; - -import lombok.ast.AstVisitor; -import lombok.ast.ConstructorInvocation; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.TypeReference; -import lombok.ast.TypeReferencePart; -import lombok.ast.VariableReference; - -/** - * Checks for common icon problems, such as wrong icon sizes, placing icons in the - * density independent drawable folder, etc. - */ -public class IconDetector extends ResourceXmlDetector implements Detector.JavaScanner { - - private static final boolean INCLUDE_LDPI; - static { - boolean includeLdpi = false; - - String value = System.getenv("ANDROID_LINT_INCLUDE_LDPI"); //$NON-NLS-1$ - if (value != null) { - includeLdpi = Boolean.valueOf(value); - } - INCLUDE_LDPI = includeLdpi; - } - - /** Pattern for the expected density folders to be found in the project */ - private static final Pattern DENSITY_PATTERN = Pattern.compile( - "^drawable-(nodpi|xhdpi|hdpi|mdpi" //$NON-NLS-1$ - + (INCLUDE_LDPI ? "|ldpi" : "") + ")$"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - private static final String[] REQUIRED_DENSITIES = INCLUDE_LDPI - ? new String[] { DRAWABLE_LDPI, DRAWABLE_MDPI, DRAWABLE_HDPI, DRAWABLE_XHDPI } - : new String[] { DRAWABLE_MDPI, DRAWABLE_HDPI, DRAWABLE_XHDPI }; - - private static final String[] DENSITY_QUALIFIERS = - new String[] { - "-ldpi", //$NON-NLS-1$ - "-mdpi", //$NON-NLS-1$ - "-hdpi", //$NON-NLS-1$ - "-xhdpi" //$NON-NLS-1$ - }; - - /** Scope needed to detect the types of icons (which involves scanning .java files, - * the manifest, menu files etc to see how icons are used - */ - private static final EnumSet<Scope> ICON_TYPE_SCOPE = EnumSet.of(Scope.ALL_RESOURCE_FILES, - Scope.JAVA_FILE, Scope.MANIFEST); - - /** Wrong icon size according to published conventions */ - public static final Issue ICON_EXPECTED_SIZE = Issue.create( - "IconExpectedSize", //$NON-NLS-1$ - "Ensures that launcher icons, notification icons etc have the correct size", - "There are predefined sizes (for each density) for launcher icons. You " + - "should follow these conventions to make sure your icons fit in with the " + - "overall look of the platform.", - Category.ICONS, - 5, - Severity.WARNING, - IconDetector.class, - ICON_TYPE_SCOPE) - // Still some potential false positives: - .setEnabledByDefault(false) - .setMoreInfo( - "http://developer.android.com/design/style/iconography.html"); //$NON-NLS-1$ - - /** Inconsistent dip size across densities */ - public static final Issue ICON_DIP_SIZE = Issue.create( - "IconDipSize", //$NON-NLS-1$ - "Ensures that icons across densities provide roughly the same density-independent size", - "Checks the all icons which are provided in multiple densities, all compute to " + - "roughly the same density-independent pixel (`dip`) size. This catches errors where " + - "images are either placed in the wrong folder, or icons are changed to new sizes " + - "but some folders are forgotten.", - Category.ICONS, - 5, - Severity.WARNING, - IconDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Images in res/drawable folder */ - public static final Issue ICON_LOCATION = Issue.create( - "IconLocation", //$NON-NLS-1$ - "Ensures that images are not defined in the density-independent drawable folder", - "The res/drawable folder is intended for density-independent graphics such as " + - "shapes defined in XML. For bitmaps, move it to `drawable-mdpi` and consider " + - "providing higher and lower resolution versions in `drawable-ldpi`, `drawable-hdpi` " + - "and `drawable-xhdpi`. If the icon *really* is density independent (for example " + - "a solid color) you can place it in `drawable-nodpi`.", - Category.ICONS, - 5, - Severity.WARNING, - IconDetector.class, - Scope.ALL_RESOURCES_SCOPE).setMoreInfo( - "http://developer.android.com/guide/practices/screens_support.html"); //$NON-NLS-1$ - - /** Missing density versions of image */ - public static final Issue ICON_DENSITIES = Issue.create( - "IconDensities", //$NON-NLS-1$ - "Ensures that icons provide custom versions for all supported densities", - "Icons will look best if a custom version is provided for each of the " + - "major screen density classes (low, medium, high, extra high). " + - "This lint check identifies icons which do not have complete coverage " + - "across the densities.\n" + - "\n" + - "Low density is not really used much anymore, so this check ignores " + - "the ldpi density. To force lint to include it, set the environment " + - "variable `ANDROID_LINT_INCLUDE_LDPI=true`. For more information on " + - "current density usage, see " + - "http://developer.android.com/resources/dashboard/screens.html", - Category.ICONS, - 4, - Severity.WARNING, - IconDetector.class, - Scope.ALL_RESOURCES_SCOPE).setMoreInfo( - "http://developer.android.com/guide/practices/screens_support.html"); //$NON-NLS-1$ - - /** Missing density folders */ - public static final Issue ICON_MISSING_FOLDER = Issue.create( - "IconMissingDensityFolder", //$NON-NLS-1$ - "Ensures that all the density folders are present", - "Icons will look best if a custom version is provided for each of the " + - "major screen density classes (low, medium, high, extra high). " + - "This lint check identifies folders which are missing, such as `drawable-hdpi`." + - "\n" + - "Low density is not really used much anymore, so this check ignores " + - "the ldpi density. To force lint to include it, set the environment " + - "variable `ANDROID_LINT_INCLUDE_LDPI=true`. For more information on " + - "current density usage, see " + - "http://developer.android.com/resources/dashboard/screens.html", - Category.ICONS, - 3, - Severity.WARNING, - IconDetector.class, - Scope.ALL_RESOURCES_SCOPE).setMoreInfo( - "http://developer.android.com/guide/practices/screens_support.html"); //$NON-NLS-1$ - - /** Using .gif bitmaps */ - public static final Issue GIF_USAGE = Issue.create( - "GifUsage", //$NON-NLS-1$ - "Checks for images using the GIF file format which is discouraged", - "The `.gif` file format is discouraged. Consider using `.png` (preferred) " + - "or `.jpg` (acceptable) instead.", - Category.ICONS, - 5, - Severity.WARNING, - IconDetector.class, - Scope.ALL_RESOURCES_SCOPE).setMoreInfo( - "http://developer.android.com/guide/topics/resources/drawable-resource.html#Bitmap"); //$NON-NLS-1$ - - /** Duplicated icons across different names */ - public static final Issue DUPLICATES_NAMES = Issue.create( - "IconDuplicates", //$NON-NLS-1$ - "Finds duplicated icons under different names", - "If an icon is repeated under different names, you can consolidate and just " + - "use one of the icons and delete the others to make your application smaller. " + - "However, duplicated icons usually are not intentional and can sometimes point " + - "to icons that were accidentally overwritten or accidentally not updated.", - Category.ICONS, - 3, - Severity.WARNING, - IconDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Duplicated contents across configurations for a given name */ - public static final Issue DUPLICATES_CONFIGURATIONS = Issue.create( - "IconDuplicatesConfig", //$NON-NLS-1$ - "Finds icons that have identical bitmaps across various configuration parameters", - "If an icon is provided under different configuration parameters such as " + - "`drawable-hdpi` or `-v11`, they should typically be different. This detector " + - "catches cases where the same icon is provided in different configuration folder " + - "which is usually not intentional.", - Category.ICONS, - 5, - Severity.WARNING, - IconDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Icons appearing in both -nodpi and a -Ndpi folder */ - public static final Issue ICON_NODPI = Issue.create( - "IconNoDpi", //$NON-NLS-1$ - "Finds icons that appear in both a -nodpi folder and a dpi folder", - "Bitmaps that appear in `drawable-nodpi` folders will not be scaled by the " + - "Android framework. If a drawable resource of the same name appears *both* in " + - "a `-nodpi` folder as well as a dpi folder such as `drawable-hdpi`, then " + - "the behavior is ambiguous and probably not intentional. Delete one or the " + - "other, or use different names for the icons.", - Category.ICONS, - 7, - Severity.WARNING, - IconDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Icons appearing as both drawable xml files and bitmaps */ - public static final Issue ICON_XML_AND_PNG = Issue.create( - "IconXmlAndPng", //$NON-NLS-1$ - "Finds icons that appear both as a drawable .xml file and as bitmaps", - "If a drawable resource appears as an .xml file in the drawable/ folder, " + - "it's usually not intentional for it to also appear as a bitmap using the " + - "same name; generally you expect the drawable XML file to define states " + - "and each state has a corresponding drawable bitmap.", - Category.ICONS, - 7, - Severity.WARNING, - IconDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Wrong filename according to the format */ - public static final Issue ICON_EXTENSION = Issue.create( - "IconExtension", //$NON-NLS-1$ - "Checks that the icon file extension matches the actual image format in the file", - - "Ensures that icons have the correct file extension (e.g. a .png file is " + - "really in the PNG format and not for example a GIF file named .png.)", - Category.ICONS, - 3, - Severity.WARNING, - IconDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Wrong filename according to the format */ - public static final Issue ICON_COLORS = Issue.create( - "IconColors", //$NON-NLS-1$ - "Checks that icons follow the recommended visual style", - - "Notification icons and Action Bar icons should only white and shades of gray. " + - "See the Android Design Guide for more details. " + - "Note that the way Lint decides whether an icon is an action bar icon or " + - "a notification icon is based on the filename prefix: `ic_menu_` for " + - "action bar icons, `ic_stat_` for notification icons etc. These correspond " + - "to the naming conventions documented in " + - "http://developer.android.com/guide/practices/ui_guidelines/icon_design.html", - Category.ICONS, - 6, - Severity.WARNING, - IconDetector.class, - ICON_TYPE_SCOPE).setMoreInfo( - "http://developer.android.com/design/style/iconography.html"); //$NON-NLS-1$ - - /** Wrong launcher icon shape */ - public static final Issue ICON_LAUNCHER_SHAPE = Issue.create( - "IconLauncherShape", //$NON-NLS-1$ - "Checks that launcher icons follow the recommended visual style", - - "According to the Android Design Guide " + - "(http://developer.android.com/design/style/iconography.html) " + - "your launcher icons should \"use a distinct silhouette\", " + - "a \"three-dimensional, front view, with a slight perspective as if viewed " + - "from above, so that users perceive some depth.\"\n" + - "\n" + - "The unique silhouette implies that your launcher icon should not be a filled " + - "square.", - Category.ICONS, - 6, - Severity.WARNING, - IconDetector.class, - ICON_TYPE_SCOPE).setMoreInfo( - "http://developer.android.com/design/style/iconography.html"); //$NON-NLS-1$ - - /** Constructs a new {@link IconDetector} check */ - public IconDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.SLOW; - } - - @Override - public void beforeCheckProject(@NonNull Context context) { - mLauncherIcons = null; - mActionBarIcons = null; - mNotificationIcons = null; - } - - @Override - public void afterCheckLibraryProject(@NonNull Context context) { - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - checkResourceFolder(context, context.getProject()); - } - - @Override - public void afterCheckProject(@NonNull Context context) { - checkResourceFolder(context, context.getProject()); - } - - private void checkResourceFolder(Context context, @NonNull Project project) { - List<File> resourceFolders = project.getResourceFolders(); - for (File res : resourceFolders) { - File[] folders = res.listFiles(); - if (folders != null) { - boolean checkFolders = context.isEnabled(ICON_DENSITIES) - || context.isEnabled(ICON_MISSING_FOLDER) - || context.isEnabled(ICON_NODPI) - || context.isEnabled(ICON_XML_AND_PNG); - boolean checkDipSizes = context.isEnabled(ICON_DIP_SIZE); - boolean checkDuplicates = context.isEnabled(DUPLICATES_NAMES) - || context.isEnabled(DUPLICATES_CONFIGURATIONS); - - Map<File, Dimension> pixelSizes = null; - Map<File, Long> fileSizes = null; - if (checkDipSizes || checkDuplicates) { - pixelSizes = new HashMap<File, Dimension>(); - fileSizes = new HashMap<File, Long>(); - } - Map<File, Set<String>> folderToNames = new HashMap<File, Set<String>>(); - Map<File, Set<String>> nonDpiFolderNames = new HashMap<File, Set<String>>(); - for (File folder : folders) { - String folderName = folder.getName(); - if (folderName.startsWith(DRAWABLE_FOLDER)) { - File[] files = folder.listFiles(); - if (files != null) { - checkDrawableDir(context, folder, files, pixelSizes, fileSizes); - - if (checkFolders && DENSITY_PATTERN.matcher(folderName).matches()) { - Set<String> names = new HashSet<String>(files.length); - for (File f : files) { - String name = f.getName(); - if (isDrawableFile(name)) { - names.add(name); - } - } - folderToNames.put(folder, names); - } else if (checkFolders) { - Set<String> names = new HashSet<String>(files.length); - for (File f : files) { - String name = f.getName(); - if (isDrawableFile(name)) { - names.add(name); - } - } - nonDpiFolderNames.put(folder, names); - } - } - } - } - - if (checkDipSizes) { - checkDipSizes(context, pixelSizes); - } - - if (checkDuplicates) { - checkDuplicates(context, pixelSizes, fileSizes); - } - - if (checkFolders && !folderToNames.isEmpty()) { - checkDensities(context, res, folderToNames, nonDpiFolderNames); - } - } - } - } - - private static boolean isDrawableFile(String name) { - // endsWith(name, DOT_PNG) is also true for endsWith(name, DOT_9PNG) - return endsWith(name, DOT_PNG)|| endsWith(name, DOT_JPG) || endsWith(name, DOT_GIF) - || endsWith(name, DOT_XML) || endsWith(name, DOT_JPEG); - } - - // This method looks for duplicates in the assets. This uses two pieces of information - // (file sizes and image dimensions) to quickly reject candidates, such that it only - // needs to check actual file contents on a small subset of the available files. - private static void checkDuplicates(Context context, Map<File, Dimension> pixelSizes, - Map<File, Long> fileSizes) { - Map<Long, Set<File>> sameSizes = new HashMap<Long, Set<File>>(); - Map<Long, File> seenSizes = new HashMap<Long, File>(fileSizes.size()); - for (Map.Entry<File, Long> entry : fileSizes.entrySet()) { - File file = entry.getKey(); - Long size = entry.getValue(); - if (seenSizes.containsKey(size)) { - Set<File> set = sameSizes.get(size); - if (set == null) { - set = new HashSet<File>(); - set.add(seenSizes.get(size)); - sameSizes.put(size, set); - } - set.add(file); - } else { - seenSizes.put(size, file); - } - } - - if (sameSizes.isEmpty()) { - return; - } - - // Now go through the files that have the same size and check to see if we can - // split them apart based on image dimensions - // Note: we may not have file sizes on all the icons; in particular, - // we don't have file sizes for ninepatch files. - Collection<Set<File>> candidateLists = sameSizes.values(); - for (Set<File> candidates : candidateLists) { - Map<Dimension, Set<File>> sameDimensions = new HashMap<Dimension, Set<File>>( - candidates.size()); - List<File> noSize = new ArrayList<File>(); - for (File file : candidates) { - Dimension dimension = pixelSizes.get(file); - if (dimension != null) { - Set<File> set = sameDimensions.get(dimension); - if (set == null) { - set = new HashSet<File>(); - sameDimensions.put(dimension, set); - } - set.add(file); - } else { - noSize.add(file); - } - } - - - // Files that we have no dimensions for must be compared against everything - Collection<Set<File>> sets = sameDimensions.values(); - if (!noSize.isEmpty()) { - if (!sets.isEmpty()) { - for (Set<File> set : sets) { - set.addAll(noSize); - } - } else { - // Must just test the noSize elements against themselves - HashSet<File> noSizeSet = new HashSet<File>(noSize); - sets = Collections.<Set<File>>singletonList(noSizeSet); - } - } - - // Map from file to actual byte contents of the file. - // We store this in a map such that for repeated files, such as noSize files - // which can appear in multiple buckets, we only need to read them once - Map<File, byte[]> fileContents = new HashMap<File, byte[]>(); - - // Now we're ready for the final check where we actually check the - // bits. We have to partition the files into buckets of files that - // are identical. - for (Set<File> set : sets) { - if (set.size() < 2) { - continue; - } - - // Read all files in this set and store in map - for (File file : set) { - byte[] bits = fileContents.get(file); - if (bits == null) { - try { - bits = context.getClient().readBytes(file); - fileContents.put(file, bits); - } catch (IOException e) { - context.log(e, null); - } - } - } - - // Map where the key file is known to be equal to the value file. - // After we check individual files for equality this will be used - // to look for transitive equality. - Map<File, File> equal = new HashMap<File, File>(); - - // Now go and compare all the files. This isn't an efficient algorithm - // but the number of candidates should be very small - - List<File> files = new ArrayList<File>(set); - Collections.sort(files); - for (int i = 0; i < files.size() - 1; i++) { - for (int j = i + 1; j < files.size(); j++) { - File file1 = files.get(i); - File file2 = files.get(j); - byte[] contents1 = fileContents.get(file1); - byte[] contents2 = fileContents.get(file2); - if (contents1 == null || contents2 == null) { - // File couldn't be read: ignore - continue; - } - if (contents1.length != contents2.length) { - // Sizes differ: not identical. - // This shouldn't happen since we've already partitioned based - // on File.length(), but just make sure here since the file - // system could have lied, or cached a value that has changed - // if the file was just overwritten - continue; - } - boolean same = true; - for (int k = 0; k < contents1.length; k++) { - if (contents1[k] != contents2[k]) { - same = false; - break; - } - } - if (same) { - equal.put(file1, file2); - } - } - } - - if (!equal.isEmpty()) { - Map<File, Set<File>> partitions = new HashMap<File, Set<File>>(); - List<Set<File>> sameSets = new ArrayList<Set<File>>(); - for (Map.Entry<File, File> entry : equal.entrySet()) { - File file1 = entry.getKey(); - File file2 = entry.getValue(); - Set<File> set1 = partitions.get(file1); - Set<File> set2 = partitions.get(file2); - if (set1 != null) { - set1.add(file2); - } else if (set2 != null) { - set2.add(file1); - } else { - set = new HashSet<File>(); - sameSets.add(set); - set.add(file1); - set.add(file2); - partitions.put(file1, set); - partitions.put(file2, set); - } - } - - // We've computed the partitions of equal files. Now sort them - // for stable output. - List<List<File>> lists = new ArrayList<List<File>>(); - for (Set<File> same : sameSets) { - assert !same.isEmpty(); - ArrayList<File> sorted = new ArrayList<File>(same); - Collections.sort(sorted); - lists.add(sorted); - } - // Sort overall partitions by the first item in each list - Collections.sort(lists, new Comparator<List<File>>() { - @Override - public int compare(List<File> list1, List<File> list2) { - return list1.get(0).compareTo(list2.get(0)); - } - }); - - for (List<File> sameFiles : lists) { - Location location = null; - boolean sameNames = true; - String lastName = null; - for (File file : sameFiles) { - if (lastName != null && !lastName.equals(file.getName())) { - sameNames = false; - } - lastName = file.getName(); - // Chain locations together - Location linkedLocation = location; - location = Location.create(file); - location.setSecondary(linkedLocation); - } - - if (sameNames) { - StringBuilder sb = new StringBuilder(sameFiles.size() * 16); - for (File file : sameFiles) { - if (sb.length() > 0) { - sb.append(", "); //$NON-NLS-1$ - } - sb.append(file.getParentFile().getName()); - } - String message = String.format( - "The %1$s icon has identical contents in the following configuration folders: %2$s", - lastName, sb.toString()); - context.report(DUPLICATES_CONFIGURATIONS, location, message, null); - } else { - StringBuilder sb = new StringBuilder(sameFiles.size() * 16); - for (File file : sameFiles) { - if (sb.length() > 0) { - sb.append(", "); //$NON-NLS-1$ - } - sb.append(file.getName()); - } - String message = String.format( - "The following unrelated icon files have identical contents: %1$s", - sb.toString()); - context.report(DUPLICATES_NAMES, location, message, null); - } - } - } - } - } - - } - - // This method checks the given map from resource file to pixel dimensions for each - // such image and makes sure that the normalized dip sizes across all the densities - // are mostly the same. - private static void checkDipSizes(Context context, Map<File, Dimension> pixelSizes) { - // Partition up the files such that I can look at a series by name. This - // creates a map from filename (such as foo.png) to a list of files - // providing that icon in various folders: drawable-mdpi/foo.png, drawable-hdpi/foo.png - // etc. - Map<String, List<File>> nameToFiles = new HashMap<String, List<File>>(); - for (File file : pixelSizes.keySet()) { - String name = file.getName(); - List<File> list = nameToFiles.get(name); - if (list == null) { - list = new ArrayList<File>(); - nameToFiles.put(name, list); - } - list.add(file); - } - - ArrayList<String> names = new ArrayList<String>(nameToFiles.keySet()); - Collections.sort(names); - - // We have to partition the files further because it's possible for the project - // to have different configurations for an icon, such as this: - // drawable-large-hdpi/foo.png, drawable-large-mdpi/foo.png, - // drawable-hdpi/foo.png, drawable-mdpi/foo.png, - // drawable-hdpi-v11/foo.png and drawable-mdpi-v11/foo.png. - // In this case we don't want to compare across categories; we want to - // ensure that the drawable-large-{density} icons are consistent, - // that the drawable-{density}-v11 icons are consistent, and that - // the drawable-{density} icons are consistent. - - // Map from name to list of map from parent folder to list of files - Map<String, Map<String, List<File>>> configMap = - new HashMap<String, Map<String,List<File>>>(); - for (Map.Entry<String, List<File>> entry : nameToFiles.entrySet()) { - String name = entry.getKey(); - List<File> files = entry.getValue(); - for (File file : files) { - String parentName = file.getParentFile().getName(); - // Strip out the density part - int index = -1; - for (String qualifier : DENSITY_QUALIFIERS) { - index = parentName.indexOf(qualifier); - if (index != -1) { - parentName = parentName.substring(0, index) - + parentName.substring(index + qualifier.length()); - break; - } - } - if (index == -1) { - // No relevant qualifier found in the parent directory name, - // e.g. it's just "drawable" or something like "drawable-nodpi". - continue; - } - - Map<String, List<File>> folderMap = configMap.get(name); - if (folderMap == null) { - folderMap = new HashMap<String,List<File>>(); - configMap.put(name, folderMap); - } - // Map from name to a map from parent folder to files - List<File> list = folderMap.get(parentName); - if (list == null) { - list = new ArrayList<File>(); - folderMap.put(parentName, list); - } - list.add(file); - } - } - - for (String name : names) { - //List<File> files = nameToFiles.get(name); - Map<String, List<File>> configurations = configMap.get(name); - if (configurations == null) { - // Nothing in this configuration: probably only found in drawable/ or - // drawable-nodpi etc directories. - continue; - } - - for (Map.Entry<String, List<File>> entry : configurations.entrySet()) { - List<File> files = entry.getValue(); - - // Ensure that all the dip sizes are *roughly* the same - Map<File, Dimension> dipSizes = new HashMap<File, Dimension>(); - int dipWidthSum = 0; // Incremental computation of average - int dipHeightSum = 0; // Incremental computation of average - int count = 0; - for (File file : files) { - float factor = getMdpiScalingFactor(file.getParentFile().getName()); - if (factor > 0) { - Dimension size = pixelSizes.get(file); - if (size == null) { - continue; - } - Dimension dip = new Dimension( - Math.round(size.width / factor), - Math.round(size.height / factor)); - dipWidthSum += dip.width; - dipHeightSum += dip.height; - dipSizes.put(file, dip); - count++; - } - } - if (count == 0) { - // Icons in drawable/ and drawable-nodpi/ - continue; - } - int meanWidth = dipWidthSum / count; - int meanHeight = dipHeightSum / count; - - // Compute standard deviation? - int squareWidthSum = 0; - int squareHeightSum = 0; - for (Dimension size : dipSizes.values()) { - squareWidthSum += (size.width - meanWidth) * (size.width - meanWidth); - squareHeightSum += (size.height - meanHeight) * (size.height - meanHeight); - } - double widthStdDev = Math.sqrt(squareWidthSum / count); - double heightStdDev = Math.sqrt(squareHeightSum / count); - - if (widthStdDev > meanWidth / 10 || heightStdDev > meanHeight) { - Location location = null; - StringBuilder sb = new StringBuilder(100); - - // Sort entries by decreasing dip size - List<Map.Entry<File, Dimension>> entries = - new ArrayList<Map.Entry<File,Dimension>>(); - for (Map.Entry<File, Dimension> entry2 : dipSizes.entrySet()) { - entries.add(entry2); - } - Collections.sort(entries, - new Comparator<Map.Entry<File, Dimension>>() { - @Override - public int compare(Entry<File, Dimension> e1, - Entry<File, Dimension> e2) { - Dimension d1 = e1.getValue(); - Dimension d2 = e2.getValue(); - if (d1.width != d2.width) { - return d2.width - d1.width; - } - - return d2.height - d1.height; - } - }); - for (Map.Entry<File, Dimension> entry2 : entries) { - if (sb.length() > 0) { - sb.append(", "); - } - File file = entry2.getKey(); - - // Chain locations together - Location linkedLocation = location; - location = Location.create(file); - location.setSecondary(linkedLocation); - Dimension dip = entry2.getValue(); - Dimension px = pixelSizes.get(file); - String fileName = file.getParentFile().getName() + File.separator - + file.getName(); - sb.append(String.format("%1$s: %2$dx%3$d dp (%4$dx%5$d px)", - fileName, dip.width, dip.height, px.width, px.height)); - } - String message = String.format( - "The image %1$s varies significantly in its density-independent (dip) " + - "size across the various density versions: %2$s", - name, sb.toString()); - context.report(ICON_DIP_SIZE, location, message, null); - } - } - } - } - - private static void checkDensities(Context context, File res, - Map<File, Set<String>> folderToNames, - Map<File, Set<String>> nonDpiFolderNames) { - // TODO: Is there a way to look at the manifest and figure out whether - // all densities are expected to be needed? - // Note: ldpi is probably not needed; it has very little usage - // (about 2%; http://developer.android.com/resources/dashboard/screens.html) - // TODO: Use the matrix to check out if we can eliminate densities based - // on the target screens? - - Set<String> definedDensities = new HashSet<String>(); - for (File f : folderToNames.keySet()) { - definedDensities.add(f.getName()); - } - - // Look for missing folders -- if you define say drawable-mdpi then you - // should also define -hdpi and -xhdpi. - if (context.isEnabled(ICON_MISSING_FOLDER)) { - List<String> missing = new ArrayList<String>(); - for (String density : REQUIRED_DENSITIES) { - if (!definedDensities.contains(density)) { - missing.add(density); - } - } - if (!missing.isEmpty()) { - context.report( - ICON_MISSING_FOLDER, - Location.create(res), - String.format("Missing density variation folders in %1$s: %2$s", - context.getProject().getDisplayPath(res), - LintUtils.formatList(missing, -1)), - null); - } - } - - if (context.isEnabled(ICON_NODPI)) { - Set<String> noDpiNames = new HashSet<String>(); - for (Map.Entry<File, Set<String>> entry : folderToNames.entrySet()) { - if (isNoDpiFolder(entry.getKey())) { - noDpiNames.addAll(entry.getValue()); - } - } - if (!noDpiNames.isEmpty()) { - // Make sure that none of the nodpi names appear in a non-nodpi folder - Set<String> inBoth = new HashSet<String>(); - List<File> files = new ArrayList<File>(); - for (Map.Entry<File, Set<String>> entry : folderToNames.entrySet()) { - File folder = entry.getKey(); - String folderName = folder.getName(); - if (!isNoDpiFolder(folder)) { - assert DENSITY_PATTERN.matcher(folderName).matches(); - Set<String> overlap = nameIntersection(noDpiNames, entry.getValue()); - inBoth.addAll(overlap); - for (String name : overlap) { - files.add(new File(folder, name)); - } - } - } - - if (!inBoth.isEmpty()) { - List<String> list = new ArrayList<String>(inBoth); - Collections.sort(list); - - // Chain locations together - Collections.sort(files); - Location location = null; - for (File file : files) { - Location linkedLocation = location; - location = Location.create(file); - location.setSecondary(linkedLocation); - } - - context.report(ICON_NODPI, location, - String.format( - "The following images appear in both -nodpi and in a density folder: %1$s", - LintUtils.formatList(list, - context.getDriver().isAbbreviating() ? 10 : -1)), - null); - } - } - } - - if (context.isEnabled(ICON_XML_AND_PNG)) { - Map<File, Set<String>> folderMap = Maps.newHashMap(folderToNames); - folderMap.putAll(nonDpiFolderNames); - Set<String> xmlNames = Sets.newHashSetWithExpectedSize(100); - Set<String> bitmapNames = Sets.newHashSetWithExpectedSize(100); - - for (Map.Entry<File, Set<String>> entry : folderMap.entrySet()) { - Set<String> names = entry.getValue(); - for (String name : names) { - if (endsWith(name, DOT_XML)) { - xmlNames.add(name); - } else if (isDrawableFile(name)) { - bitmapNames.add(name); - } - } - } - if (!xmlNames.isEmpty() && !bitmapNames.isEmpty()) { - // Make sure that none of the nodpi names appear in a non-nodpi folder - Set<String> overlap = nameIntersection(xmlNames, bitmapNames); - if (!overlap.isEmpty()) { - Multimap<String, File> map = ArrayListMultimap.create(); - Set<String> bases = Sets.newHashSetWithExpectedSize(overlap.size()); - for (String name : overlap) { - bases.add(LintUtils.getBaseName(name)); - } - - for (String base : bases) { - for (Map.Entry<File, Set<String>> entry : folderMap.entrySet()) { - File folder = entry.getKey(); - for (String n : entry.getValue()) { - if (base.equals(LintUtils.getBaseName(n))) { - map.put(base, new File(folder, n)); - } - } - } - } - List<String> sorted = new ArrayList<String>(map.keySet()); - Collections.sort(sorted); - for (String name : sorted) { - List<File> lists = Lists.newArrayList(map.get(name)); - Collections.sort(lists); - - // Chain locations together - Location location = null; - for (File file : lists) { - Location linkedLocation = location; - location = Location.create(file); - location.setSecondary(linkedLocation); - } - - List<String> fileNames = Lists.newArrayList(); - boolean seenXml = false; - boolean seenNonXml = false; - for (File f : lists) { - boolean isXml = endsWith(f.getPath(), DOT_XML); - if (isXml && !seenXml) { - fileNames.add(context.getProject().getDisplayPath(f)); - seenXml = true; - } else if (!isXml && !seenNonXml) { - fileNames.add(context.getProject().getDisplayPath(f)); - seenNonXml = true; - } - } - - context.report(ICON_XML_AND_PNG, location, - String.format( - "The following images appear both as density independent .xml files and as bitmap files: %1$s", - LintUtils.formatList(fileNames, - context.getDriver().isAbbreviating() ? 10 : -1)), - null); - } - } - } - } - - if (context.isEnabled(ICON_DENSITIES)) { - // Look for folders missing some of the specific assets - Set<String> allNames = new HashSet<String>(); - for (Entry<File,Set<String>> entry : folderToNames.entrySet()) { - if (!isNoDpiFolder(entry.getKey())) { - Set<String> names = entry.getValue(); - allNames.addAll(names); - } - } - - for (Map.Entry<File, Set<String>> entry : folderToNames.entrySet()) { - File file = entry.getKey(); - if (isNoDpiFolder(file)) { - continue; - } - Set<String> names = entry.getValue(); - if (names.size() != allNames.size()) { - List<String> delta = new ArrayList<String>(nameDifferences(allNames, names)); - if (delta.isEmpty()) { - continue; - } - Collections.sort(delta); - String foundIn = ""; - if (delta.size() == 1) { - // Produce list of where the icon is actually defined - List<String> defined = new ArrayList<String>(); - String name = delta.get(0); - for (Map.Entry<File, Set<String>> e : folderToNames.entrySet()) { - if (e.getValue().contains(name)) { - defined.add(e.getKey().getName()); - } - } - if (!defined.isEmpty()) { - foundIn = String.format(" (found in %1$s)", - LintUtils.formatList(defined, - context.getDriver().isAbbreviating() ? 5 : -1)); - } - } - - context.report(ICON_DENSITIES, Location.create(file), - String.format( - "Missing the following drawables in %1$s: %2$s%3$s", - file.getName(), - LintUtils.formatList(delta, - context.getDriver().isAbbreviating() ? 5 : -1), - foundIn), - null); - } - } - } - } - - /** - * Compute the difference in names between a and b. This is not just - * Sets.difference(a, b) because we want to make the comparisons <b>without - * file extensions</b> and return the result <b>with</b>.. - */ - private static Set<String> nameDifferences(Set<String> a, Set<String> b) { - Set<String> names1 = new HashSet<String>(a.size()); - for (String s : a) { - names1.add(LintUtils.getBaseName(s)); - } - Set<String> names2 = new HashSet<String>(b.size()); - for (String s : b) { - names2.add(LintUtils.getBaseName(s)); - } - - names1.removeAll(names2); - - if (!names1.isEmpty()) { - // Map filenames back to original filenames with extensions - Set<String> result = new HashSet<String>(names1.size()); - for (String s : a) { - if (names1.contains(LintUtils.getBaseName(s))) { - result.add(s); - } - } - for (String s : b) { - if (names1.contains(LintUtils.getBaseName(s))) { - result.add(s); - } - } - - return result; - } - - return Collections.emptySet(); - } - - /** - * Compute the intersection in names between a and b. This is not just - * Sets.intersection(a, b) because we want to make the comparisons <b>without - * file extensions</b> and return the result <b>with</b>. - */ - private static Set<String> nameIntersection(Set<String> a, Set<String> b) { - Set<String> names1 = new HashSet<String>(a.size()); - for (String s : a) { - names1.add(LintUtils.getBaseName(s)); - } - Set<String> names2 = new HashSet<String>(b.size()); - for (String s : b) { - names2.add(LintUtils.getBaseName(s)); - } - - names1.retainAll(names2); - - if (!names1.isEmpty()) { - // Map filenames back to original filenames with extensions - Set<String> result = new HashSet<String>(names1.size()); - for (String s : a) { - if (names1.contains(LintUtils.getBaseName(s))) { - result.add(s); - } - } - for (String s : b) { - if (names1.contains(LintUtils.getBaseName(s))) { - result.add(s); - } - } - - return result; - } - - return Collections.emptySet(); - } - - private static boolean isNoDpiFolder(File file) { - return file.getName().contains("-nodpi"); - } - - private Map<File, BufferedImage> mImageCache; - - @Nullable - private BufferedImage getImage(@Nullable File file) throws IOException { - if (mImageCache == null) { - mImageCache = Maps.newHashMap(); - } else { - BufferedImage image = mImageCache.get(file); - if (image != null) { - return image; - } - } - - BufferedImage image = ImageIO.read(file); - mImageCache.put(file, image); - - return image; - } - - private void checkDrawableDir(Context context, File folder, File[] files, - Map<File, Dimension> pixelSizes, Map<File, Long> fileSizes) { - if (folder.getName().equals(DRAWABLE_FOLDER) - && context.isEnabled(ICON_LOCATION) && - // If supporting older versions than Android 1.6, it's not an error - // to include bitmaps in drawable/ - context.getProject().getMinSdk() >= 4) { - for (File file : files) { - String name = file.getName(); - if (name.endsWith(DOT_XML)) { - // pass - most common case, avoids checking other extensions - } else if (endsWith(name, DOT_PNG) - || endsWith(name, DOT_JPG) - || endsWith(name, DOT_JPEG) - || endsWith(name, DOT_GIF)) { - context.report(ICON_LOCATION, - Location.create(file), - String.format("Found bitmap drawable res/drawable/%1$s in " + - "densityless folder", - file.getName()), - null); - } - } - } - - if (context.isEnabled(GIF_USAGE)) { - for (File file : files) { - String name = file.getName(); - if (endsWith(name, DOT_GIF)) { - context.report(GIF_USAGE, Location.create(file), - "Using the .gif format for bitmaps is discouraged", - null); - } - } - } - - if (context.isEnabled(ICON_EXTENSION)) { - for (File file : files) { - String path = file.getPath(); - if (isDrawableFile(path) && !endsWith(path, DOT_XML)) { - checkExtension(context, file); - } - } - } - - if (context.isEnabled(ICON_COLORS)) { - for (File file : files) { - String name = file.getName(); - - if (isDrawableFile(name) - && !endsWith(name, DOT_XML) - && !endsWith(name, DOT_9PNG)) { - String baseName = getBaseName(name); - boolean isActionBarIcon = isActionBarIcon(context, baseName, file); - if (isActionBarIcon || isNotificationIcon(baseName)) { - Dimension size = checkColor(context, file, isActionBarIcon); - - // Store dimension for size check if we went to the trouble of reading image - if (size != null && pixelSizes != null) { - pixelSizes.put(file, size); - } - } - } - } - } - - if (context.isEnabled(ICON_LAUNCHER_SHAPE)) { - // Look up launcher icon name - for (File file : files) { - String name = file.getName(); - if (isLauncherIcon(getBaseName(name)) - && !endsWith(name, DOT_XML) - && !endsWith(name, DOT_9PNG)) { - checkLauncherShape(context, file); - } - } - } - - // Check icon sizes - if (context.isEnabled(ICON_EXPECTED_SIZE)) { - checkExpectedSizes(context, folder, files); - } - - if (pixelSizes != null || fileSizes != null) { - for (File file : files) { - // TODO: Combine this check with the check for expected sizes such that - // I don't check file sizes twice! - String fileName = file.getName(); - - if (endsWith(fileName, DOT_PNG) || endsWith(fileName, DOT_JPG) - || endsWith(fileName, DOT_JPEG)) { - // Only scan .png files (except 9-patch png's) and jpg files for - // dip sizes. Duplicate checks can also be performed on ninepatch files. - if (pixelSizes != null && !endsWith(fileName, DOT_9PNG) - && !pixelSizes.containsKey(file)) { // already read by checkColor? - Dimension size = getSize(file); - pixelSizes.put(file, size); - } - if (fileSizes != null) { - fileSizes.put(file, file.length()); - } - } - } - } - - mImageCache = null; - } - - /** - * Check that launcher icons do not fill every pixel in the image - */ - private void checkLauncherShape(Context context, File file) { - try { - BufferedImage image = getImage(file); - if (image != null) { - // TODO: see if the shape is rectangular but inset from outer rectangle; if so - // that's probably not right either! - for (int y = 0, height = image.getHeight(); y < height; y++) { - for (int x = 0, width = image.getWidth(); x < width; x++) { - int rgb = image.getRGB(x, y); - if ((rgb & 0xFF000000) == 0) { - return; - } - } - } - - String message = "Launcher icons should not fill every pixel of their square " + - "region; see the design guide for details"; - context.report(ICON_LAUNCHER_SHAPE, Location.create(file), - message, null); - } - } catch (IOException e) { - // Pass: ignore files we can't read - } - } - - /** - * Check whether the icons in the file are okay. Also return the image size - * if known (for use by other checks) - */ - private Dimension checkColor(Context context, File file, boolean isActionBarIcon) { - int folderVersion = Context.getFolderVersion(file); - if (isActionBarIcon) { - if (folderVersion != -1 && folderVersion < 11 - || !isAndroid30(context, folderVersion)) { - return null; - } - } else { - if (folderVersion != -1 && folderVersion < 9 - || !isAndroid23(context, folderVersion) - && !isAndroid30(context, folderVersion)) { - return null; - } - } - - // TODO: This only checks icons that are known to be using the Holo style. - // However, if the user has minSdk < 11 as well as targetSdk > 11, we should - // also check that they actually include a -v11 or -v14 folder with proper - // icons, since the below won't flag the older icons. - try { - BufferedImage image = getImage(file); - if (image != null) { - if (isActionBarIcon) { - checkPixels: - for (int y = 0, height = image.getHeight(); y < height; y++) { - for (int x = 0, width = image.getWidth(); x < width; x++) { - int rgb = image.getRGB(x, y); - if ((rgb & 0xFF000000) != 0) { // else: transparent - int r = (rgb & 0xFF0000) >>> 16; - int g = (rgb & 0x00FF00) >>> 8; - int b = (rgb & 0x0000FF); - if (r != g || r != b) { - String message = "Action Bar icons should use a single gray " - + "color (#333333 for light themes (with 60%/30% " - + "opacity for enabled/disabled), and #FFFFFF with " - + "opacity 80%/30% for dark themes"; - context.report(ICON_COLORS, Location.create(file), - message, null); - break checkPixels; - } - } - } - } - } else { - if (folderVersion >= 11 || isAndroid30(context, folderVersion)) { - // Notification icons. Should be white as of API 14 - checkPixels: - for (int y = 0, height = image.getHeight(); y < height; y++) { - for (int x = 0, width = image.getWidth(); x < width; x++) { - int rgb = image.getRGB(x, y); - if ((rgb & 0xFF000000) != 0 - && rgb != 0xFFFFFFFF) { - int r = (rgb & 0xFF0000) >>> 16; - int g = (rgb & 0x00FF00) >>> 8; - int b = (rgb & 0x0000FF); - if (r == g && r == b) { - // If the pixel is not white, it might be because of - // anti-aliasing. In that case, at least one neighbor - // should be of a different color - if (x < width - 1 && rgb != image.getRGB(x + 1, y)) { - continue; - } - if (x > 0 && rgb != image.getRGB(x - 1, y)) { - continue; - } - if (y < height - 1 && rgb != image.getRGB(x, y + 1)) { - continue; - } - if (y > 0 && rgb != image.getRGB(x, y - 1)) { - continue; - } - } - - String message = "Notification icons must be entirely white"; - context.report(ICON_COLORS, Location.create(file), - message, null); - break checkPixels; - } - } - } - } else { - // As of API 9, should be gray. - checkPixels: - for (int y = 0, height = image.getHeight(); y < height; y++) { - for (int x = 0, width = image.getWidth(); x < width; x++) { - int rgb = image.getRGB(x, y); - if ((rgb & 0xFF000000) != 0) { // else: transparent - int r = (rgb & 0xFF0000) >>> 16; - int g = (rgb & 0x00FF00) >>> 8; - int b = (rgb & 0x0000FF); - if (r != g || r != b) { - String message = "Notification icons should not use " - + "colors"; - context.report(ICON_COLORS, Location.create(file), - message, null); - break checkPixels; - } - } - } - } - } - } - - return new Dimension(image.getWidth(), image.getHeight()); - } - } catch (IOException e) { - // Pass: ignore files we can't read - } - - return null; - } - - private static void checkExtension(Context context, File file) { - try { - ImageInputStream input = ImageIO.createImageInputStream(file); - if (input != null) { - try { - Iterator<ImageReader> readers = ImageIO.getImageReaders(input); - while (readers.hasNext()) { - ImageReader reader = readers.next(); - try { - reader.setInput(input); - - // Check file extension - String formatName = reader.getFormatName(); - if (formatName != null && !formatName.isEmpty()) { - String path = file.getPath(); - int index = path.lastIndexOf('.'); - String extension = path.substring(index+1).toLowerCase(Locale.US); - - if (!formatName.equalsIgnoreCase(extension)) { - if (endsWith(path, DOT_JPG) - && formatName.equals("JPEG")) { //$NON-NLS-1$ - return; - } - String message = String.format( - "Misleading file extension; named .%1$s but the " + - "file format is %2$s", extension, formatName); - Location location = Location.create(file); - context.report(ICON_EXTENSION, location, message, null); - } - break; - } - } finally { - reader.dispose(); - } - } - } finally { - if (input != null) { - input.close(); - } - } - } - } catch (IOException e) { - // Pass -- we can't handle all image types, warn about those we can - } - } - - private static String getBaseName(String name) { - String baseName = name; - int index = baseName.indexOf('.'); - if (index != -1) { - baseName = baseName.substring(0, index); - } - - return baseName; - } - - private void checkExpectedSizes(Context context, File folder, File[] files) { - if (files == null || files.length == 0) { - return; - } - - String folderName = folder.getName(); - int folderVersion = Context.getFolderVersion(files[0]); - - for (File file : files) { - String name = file.getName(); - - // TODO: Look up exact app icon from the manifest rather than simply relying on - // the naming conventions described here: - // http://developer.android.com/guide/practices/ui_guidelines/icon_design.html#design-tips - // See if we can figure out other types of icons from usage too. - - String baseName = getBaseName(name); - - if (isLauncherIcon(baseName)) { - // Launcher icons - checkSize(context, folderName, file, 48, 48, true /*exact*/); - } else if (isActionBarIcon(baseName)) { - checkSize(context, folderName, file, 32, 32, true /*exact*/); - } else if (name.startsWith("ic_dialog_")) { //$NON-NLS-1$ - // Dialog - checkSize(context, folderName, file, 32, 32, true /*exact*/); - } else if (name.startsWith("ic_tab_")) { //$NON-NLS-1$ - // Tab icons - checkSize(context, folderName, file, 32, 32, true /*exact*/); - } else if (isNotificationIcon(baseName)) { - // Notification icons - if (isAndroid30(context, folderVersion)) { - checkSize(context, folderName, file, 24, 24, true /*exact*/); - } else if (isAndroid23(context, folderVersion)) { - checkSize(context, folderName, file, 16, 25, false /*exact*/); - } else { - // Android 2.2 or earlier - // TODO: Should this be done for each folder size? - checkSize(context, folderName, file, 25, 25, true /*exact*/); - } - } else if (name.startsWith("ic_menu_")) { //$NON-NLS-1$ - if (isAndroid30(context, folderVersion)) { - // Menu icons (<=2.3 only: Replaced by action bar icons (ic_action_ in 3.0). - // However the table halfway down the page on - // http://developer.android.com/guide/practices/ui_guidelines/icon_design.html - // and the README in the icon template download says that convention is ic_menu - checkSize(context, folderName, file, 32, 32, true); - } else if (isAndroid23(context, folderVersion)) { - // The icon should be 32x32 inside the transparent image; should - // we check that this is mostly the case (a few pixels are allowed to - // overlap for anti-aliasing etc) - checkSize(context, folderName, file, 48, 48, true /*exact*/); - } else { - // Android 2.2 or earlier - // TODO: Should this be done for each folder size? - checkSize(context, folderName, file, 48, 48, true /*exact*/); - } - } - // TODO: ListView icons? - } - } - - /** - * Is this drawable folder for an Android 3.0 drawable? This will be the - * case if it specifies -v11+, or if the minimum SDK version declared in the - * manifest is at least 11. - */ - private static boolean isAndroid30(Context context, int folderVersion) { - return folderVersion >= 11 || context.getMainProject().getMinSdk() >= 11; - } - - /** - * Is this drawable folder for an Android 2.3 drawable? This will be the - * case if it specifies -v9 or -v10, or if the minimum SDK version declared in the - * manifest is 9 or 10 (and it does not specify some higher version like -v11 - */ - private static boolean isAndroid23(Context context, int folderVersion) { - if (isAndroid30(context, folderVersion)) { - return false; - } - - if (folderVersion == 9 || folderVersion == 10) { - return true; - } - - int minSdk = context.getMainProject().getMinSdk(); - - return minSdk == 9 || minSdk == 10; - } - - private static float getMdpiScalingFactor(String folderName) { - // Can't do startsWith(DRAWABLE_MDPI) because the folder could - // be something like "drawable-sw600dp-mdpi". - if (folderName.contains("-mdpi")) { //$NON-NLS-1$ - return 1.0f; - } else if (folderName.contains("-hdpi")) { //$NON-NLS-1$ - return 1.5f; - } else if (folderName.contains("-xhdpi")) { //$NON-NLS-1$ - return 2.0f; - } else if (folderName.contains("-ldpi")) { //$NON-NLS-1$ - return 0.75f; - } else { - return 0f; - } - } - - private static void checkSize(Context context, String folderName, File file, - int mdpiWidth, int mdpiHeight, boolean exactMatch) { - String fileName = file.getName(); - // Only scan .png files (except 9-patch png's) and jpg files - if (!((endsWith(fileName, DOT_PNG) && !endsWith(fileName, DOT_9PNG)) || - endsWith(fileName, DOT_JPG) || endsWith(fileName, DOT_JPEG))) { - return; - } - - int width; - int height; - // Use 3:4:6:8 scaling ratio to look up the other expected sizes - if (folderName.startsWith(DRAWABLE_MDPI)) { - width = mdpiWidth; - height = mdpiHeight; - } else if (folderName.startsWith(DRAWABLE_HDPI)) { - // Perform math using floating point; if we just do - // width = mdpiWidth * 3 / 2; - // then for mdpiWidth = 25 (as in notification icons on pre-GB) we end up - // with width = 37, instead of 38 (with floating point rounding we get 37.5 = 38) - width = Math.round(mdpiWidth * 3.f / 2); - height = Math.round(mdpiHeight * 3f / 2); - } else if (folderName.startsWith(DRAWABLE_XHDPI)) { - width = mdpiWidth * 2; - height = mdpiHeight * 2; - } else if (folderName.startsWith(DRAWABLE_LDPI)) { - width = Math.round(mdpiWidth * 3f / 4); - height = Math.round(mdpiHeight * 3f / 4); - } else { - return; - } - - Dimension size = getSize(file); - if (size != null) { - if (exactMatch && size.width != width || size.height != height) { - context.report( - ICON_EXPECTED_SIZE, - Location.create(file), - String.format( - "Incorrect icon size for %1$s: expected %2$dx%3$d, but was %4$dx%5$d", - folderName + File.separator + file.getName(), - width, height, size.width, size.height), - null); - } else if (!exactMatch && size.width > width || size.height > height) { - context.report( - ICON_EXPECTED_SIZE, - Location.create(file), - String.format( - "Incorrect icon size for %1$s: icon size should be at most %2$dx%3$d, but was %4$dx%5$d", - folderName + File.separator + file.getName(), - width, height, size.width, size.height), - null); - } - } - } - - private static Dimension getSize(File file) { - try { - ImageInputStream input = ImageIO.createImageInputStream(file); - if (input != null) { - try { - Iterator<ImageReader> readers = ImageIO.getImageReaders(input); - if (readers.hasNext()) { - ImageReader reader = readers.next(); - try { - reader.setInput(input); - return new Dimension(reader.getWidth(0), reader.getHeight(0)); - } finally { - reader.dispose(); - } - } - } finally { - if (input != null) { - input.close(); - } - } - } - - // Fallback: read the image using the normal means - BufferedImage image = ImageIO.read(file); - if (image != null) { - return new Dimension(image.getWidth(), image.getHeight()); - } else { - return null; - } - } catch (IOException e) { - // Pass -- we can't handle all image types, warn about those we can - return null; - } - } - - - private Set<String> mActionBarIcons; - private Set<String> mNotificationIcons; - private Set<String> mLauncherIcons; - private Multimap<String, String> mMenuToIcons; - - private boolean isLauncherIcon(String name) { - assert name.indexOf('.') == -1; // Should supply base name - - // Naming convention - if (name.startsWith("ic_launcher")) { //$NON-NLS-1$ - return true; - } - return mLauncherIcons != null && mLauncherIcons.contains(name); - } - - private boolean isNotificationIcon(String name) { - assert name.indexOf('.') == -1; // Should supply base name - - // Naming convention - if (name.startsWith("ic_stat_")) { //$NON-NLS-1$ - return true; - } - - return mNotificationIcons != null && mNotificationIcons.contains(name); - } - - private boolean isActionBarIcon(String name) { - assert name.indexOf('.') == -1; // Should supply base name - - // Naming convention - if (name.startsWith("ic_action_")) { //$NON-NLS-1$ - return true; - } - - // Naming convention - - return mActionBarIcons != null && mActionBarIcons.contains(name); - } - - private boolean isActionBarIcon(Context context, String name, File file) { - if (isActionBarIcon(name)) { - return true; - } - - // As of Android 3.0 ic_menu_ are action icons - if (file != null && name.startsWith("ic_menu_") //$NON-NLS-1$ - && isAndroid30(context, Context.getFolderVersion(file))) { - // Naming convention - return true; - } - - return false; - } - - // XML detector: Skim manifest and menu files - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return file.getName().equals(ANDROID_MANIFEST_XML); - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.MENU; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - // Manifest - TAG_APPLICATION, - TAG_ACTIVITY, - TAG_SERVICE, - TAG_PROVIDER, - TAG_RECEIVER, - - // Menu - TAG_ITEM - ); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String icon = element.getAttributeNS(ANDROID_URI, ATTR_ICON); - if (icon != null && icon.startsWith(DRAWABLE_PREFIX)) { - icon = icon.substring(DRAWABLE_PREFIX.length()); - - String tagName = element.getTagName(); - if (tagName.equals(TAG_ITEM)) { - if (mMenuToIcons == null) { - mMenuToIcons = ArrayListMultimap.create(); - } - String menu = getBaseName(context.file.getName()); - mMenuToIcons.put(menu, icon); - } else { - // Manifest tags: launcher icons - if (mLauncherIcons == null) { - mLauncherIcons = Sets.newHashSet(); - } - mLauncherIcons.add(icon); - } - } - } - - - // ---- Implements JavaScanner ---- - - private static final String NOTIFICATION_CLASS = "Notification"; //$NON-NLS-1$ - private static final String NOTIFICATION_COMPAT_CLASS = "NotificationCompat"; //$NON-NLS-1$ - private static final String BUILDER_CLASS = "Builder"; //$NON-NLS-1$ - private static final String SET_SMALL_ICON = "setSmallIcon"; //$NON-NLS-1$ - private static final String ON_CREATE_OPTIONS_MENU = "onCreateOptionsMenu"; //$NON-NLS-1$ - - @Override - @Nullable - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - return new NotificationFinder(); - } - - @Override - @Nullable - public List<Class<? extends Node>> getApplicableNodeTypes() { - List<Class<? extends Node>> types = new ArrayList<Class<? extends Node>>(3); - types.add(MethodDeclaration.class); - types.add(ConstructorInvocation.class); - return types; - } - - private final class NotificationFinder extends ForwardingAstVisitor { - @Override - public boolean visitMethodDeclaration(MethodDeclaration node) { - if (ON_CREATE_OPTIONS_MENU.equals(node.astMethodName().astValue())) { - // Gather any R.menu references found in this method - node.accept(new MenuFinder()); - } - - return super.visitMethodDeclaration(node); - } - - @Override - public boolean visitConstructorInvocation(ConstructorInvocation node) { - TypeReference reference = node.astTypeReference(); - StrictListAccessor<TypeReferencePart, TypeReference> parts = reference.astParts(); - String typeName = parts.last().astIdentifier().astValue(); - if (NOTIFICATION_CLASS.equals(typeName)) { - StrictListAccessor<Expression, ConstructorInvocation> args = node.astArguments(); - if (args.size() == 3) { - if (args.first() instanceof Select && handleSelect((Select) args.first())) { - return super.visitConstructorInvocation(node); - } - - Node method = StringFormatDetector.getParentMethod(node); - if (method != null) { - // Must track local types - String name = StringFormatDetector.getResourceForFirstArg(method, node); - if (name != null) { - if (mNotificationIcons == null) { - mNotificationIcons = Sets.newHashSet(); - } - mNotificationIcons.add(name); - } - } - } - } else if (BUILDER_CLASS.equals(typeName)) { - boolean isBuilder = false; - if (parts.size() == 1) { - isBuilder = true; - } else if (parts.size() == 2) { - String clz = parts.first().astIdentifier().astValue(); - if (NOTIFICATION_CLASS.equals(clz) || NOTIFICATION_COMPAT_CLASS.equals(clz)) { - isBuilder = true; - } - } - if (isBuilder) { - Node method = StringFormatDetector.getParentMethod(node); - if (method != null) { - SetIconFinder finder = new SetIconFinder(); - method.accept(finder); - } - } - } - - return super.visitConstructorInvocation(node); - } - } - - private boolean handleSelect(Select select) { - if (select.toString().startsWith(R_DRAWABLE_PREFIX)) { - String name = select.astIdentifier().astValue(); - if (mNotificationIcons == null) { - mNotificationIcons = Sets.newHashSet(); - } - mNotificationIcons.add(name); - - return true; - } - - return false; - } - - private final class SetIconFinder extends ForwardingAstVisitor { - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (SET_SMALL_ICON.equals(node.astName().astValue())) { - StrictListAccessor<Expression,MethodInvocation> arguments = node.astArguments(); - if (arguments.size() == 1 && arguments.first() instanceof Select) { - handleSelect((Select) arguments.first()); - } - } - return super.visitMethodInvocation(node); - } - } - - private final class MenuFinder extends ForwardingAstVisitor { - @Override - public boolean visitSelect(Select node) { - // R.type.name - if (node.astOperand() instanceof Select) { - Select select = (Select) node.astOperand(); - if (select.astOperand() instanceof VariableReference) { - VariableReference reference = (VariableReference) select.astOperand(); - if (reference.astIdentifier().astValue().equals(R_CLASS)) { - String type = select.astIdentifier().astValue(); - - if (type.equals(MENU_TYPE)) { - String name = node.astIdentifier().astValue(); - // Reclassify icons in the given menu as action bar icons - if (mMenuToIcons != null) { - Collection<String> icons = mMenuToIcons.get(name); - if (icons != null) { - if (mActionBarIcons == null) { - mActionBarIcons = Sets.newHashSet(); - } - mActionBarIcons.addAll(icons); - } - } - } - } - } - } - - return super.visitSelect(node); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/InefficientWeightDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/InefficientWeightDetector.java deleted file mode 100644 index 0608de1..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/InefficientWeightDetector.java +++ /dev/null @@ -1,349 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_BASELINE_ALIGNED; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ATTR_ORIENTATION; -import static com.android.SdkConstants.ATTR_STYLE; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.RADIO_GROUP; -import static com.android.SdkConstants.VALUE_FILL_PARENT; -import static com.android.SdkConstants.VALUE_MATCH_PARENT; -import static com.android.SdkConstants.VALUE_VERTICAL; -import static com.android.SdkConstants.VIEW; -import static com.android.SdkConstants.VIEW_FRAGMENT; -import static com.android.SdkConstants.VIEW_INCLUDE; -import static com.android.SdkConstants.VIEW_TAG; - -import com.android.annotations.NonNull; -import com.android.tools.lint.client.api.SdkInfo; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -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.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.util.Collection; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * Checks whether a layout_weight is declared inefficiently. - */ -public class InefficientWeightDetector extends LayoutDetector { - - /** Can a weight be replaced with 0dp instead for better performance? */ - public static final Issue INEFFICIENT_WEIGHT = Issue.create( - "InefficientWeight", //$NON-NLS-1$ - "Looks for inefficient weight declarations in LinearLayouts", - "When only a single widget in a LinearLayout defines a weight, it is more " + - "efficient to assign a width/height of `0dp` to it since it will absorb all " + - "the remaining space anyway. With a declared width/height of `0dp` it " + - "does not have to measure its own size first.", - Category.PERFORMANCE, - 3, - Severity.WARNING, - InefficientWeightDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Are weights nested? */ - public static final Issue NESTED_WEIGHTS = Issue.create( - "NestedWeights", //$NON-NLS-1$ - "Looks for nested layout weights, which are costly", - "Layout weights require a widget to be measured twice. When a LinearLayout with " + - "non-zero weights is nested inside another LinearLayout with non-zero weights, " + - "then the number of measurements increase exponentially.", - Category.PERFORMANCE, - 3, - Severity.WARNING, - InefficientWeightDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Should a LinearLayout set android:baselineAligned? */ - public static final Issue BASELINE_WEIGHTS = Issue.create( - "DisableBaselineAlignment", //$NON-NLS-1$ - "Looks for LinearLayouts which should set android:baselineAligned=false", - "When a LinearLayout is used to distribute the space proportionally between " + - "nested layouts, the baseline alignment property should be turned off to " + - "make the layout computation faster.", - Category.PERFORMANCE, - 3, - Severity.WARNING, - InefficientWeightDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Using 0dp on the wrong dimension */ - public static final Issue WRONG_0DP = Issue.create( - "Suspicious0dp", //$NON-NLS-1$ - "Looks for 0dp as the width in a vertical LinearLayout or as the height in a " + - "horizontal", - - "Using 0dp as the width in a horizontal LinearLayout with weights is a useful " + - "trick to ensure that only the weights (and not the intrinsic sizes) are used " + - "when sizing the children.\n" + - "\n" + - "However, if you use 0dp for the opposite dimension, the view will be invisible. " + - "This can happen if you change the orientation of a layout without also flipping " + - "the 0dp dimension in all the children.", - Category.CORRECTNESS, - 6, - Severity.ERROR, - InefficientWeightDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Missing explicit orientation */ - public static final Issue ORIENTATION = Issue.create( - "Orientation", //$NON-NLS-1$ - "Checks that LinearLayouts with multiple children set the orientation", - - "The default orientation of a LinearLayout is horizontal. It's pretty easy to " - + "believe that the layout is vertical, add multiple children to it, and wonder " - + "why only the first child is visible (when the subsequent children are " - + "off screen to the right). This lint rule helps pinpoint this issue by " - + "warning whenever a LinearLayout is used with an implicit orientation " - + "and multiple children.", - - Category.CORRECTNESS, - 2, - Severity.ERROR, - InefficientWeightDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** - * Map from element to whether that element has a non-zero linear layout - * weight or has an ancestor which does - */ - private final Map<Node, Boolean> mInsideWeight = new IdentityHashMap<Node, Boolean>(); - - /** Constructs a new {@link InefficientWeightDetector} */ - public InefficientWeightDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList(LINEAR_LAYOUT); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - List<Element> children = LintUtils.getChildren(element); - // See if there is exactly one child with a weight - boolean multipleWeights = false; - Element weightChild = null; - boolean checkNesting = context.isEnabled(NESTED_WEIGHTS); - for (Element child : children) { - if (child.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT)) { - if (weightChild != null) { - // More than one child defining a weight! - multipleWeights = true; - } else if (!multipleWeights) { - weightChild = child; - } - - if (checkNesting) { - mInsideWeight.put(child, Boolean.TRUE); - - Boolean inside = mInsideWeight.get(element); - if (inside == null) { - mInsideWeight.put(element, Boolean.FALSE); - } else if (inside) { - Attr sizeNode = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT); - context.report(NESTED_WEIGHTS, sizeNode, - context.getLocation(sizeNode), - "Nested weights are bad for performance", null); - // Don't warn again - checkNesting = false; - } - } - } - } - - String orientation = element.getAttributeNS(ANDROID_URI, ATTR_ORIENTATION); - if (children.size() >= 2 && (orientation == null || orientation.isEmpty()) - && context.isEnabled(ORIENTATION)) { - // See if at least one of the children, except the last one, sets layout_width - // to match_parent (or fill_parent), in an implicitly horizontal layout, since - // that might mean the last child won't be visible. This is a source of confusion - // for new Android developers. - boolean maxWidthSet = false; - Iterator<Element> iterator = children.iterator(); - while (iterator.hasNext()) { - Element child = iterator.next(); - if (!iterator.hasNext()) { // Don't check the last one - break; - } - String width = child.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH); - if (VALUE_MATCH_PARENT.equals(width) || VALUE_FILL_PARENT.equals(width)) { - // Also check that weights are not set here; this affects the computation - // a bit and the child may not fill up the whole linear layout - if (!child.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT)) { - maxWidthSet = true; - break; - } - } - } - if (maxWidthSet && !element.hasAttribute(ATTR_STYLE)) { - String message = "Wrong orientation? No orientation specified, and the default " - + "is horizontal, yet this layout has multiple children where at " - + "least one has layout_width=\"match_parent\""; - context.report(ORIENTATION, element, context.getLocation(element), message, null); - } - } - - if (context.isEnabled(BASELINE_WEIGHTS) && weightChild != null - && !VALUE_VERTICAL.equals(orientation) - && !element.hasAttributeNS(ANDROID_URI, ATTR_BASELINE_ALIGNED)) { - // See if all the children are layouts - boolean allChildrenAreLayouts = !children.isEmpty(); - SdkInfo sdkInfo = context.getClient().getSdkInfo(context.getProject()); - for (Element child : children) { - String tagName = child.getTagName(); - if (!(sdkInfo.isLayout(tagName) - // RadioGroup is a layout, but one which possibly should be base aligned - && !tagName.equals(RADIO_GROUP) - // Consider <fragment> tags as layouts for the purposes of this check - || VIEW_FRAGMENT.equals(tagName) - // Ditto for <include> tags - || VIEW_INCLUDE.equals(tagName))) { - allChildrenAreLayouts = false; - } - } - if (allChildrenAreLayouts) { - context.report(BASELINE_WEIGHTS, - element, - context.getLocation(element), - "Set android:baselineAligned=\"false\" on this element for better performance", - null); - } - } - - if (context.isEnabled(INEFFICIENT_WEIGHT) - && weightChild != null && !multipleWeights) { - String dimension; - if (VALUE_VERTICAL.equals(orientation)) { - dimension = ATTR_LAYOUT_HEIGHT; - } else { - dimension = ATTR_LAYOUT_WIDTH; - } - Attr sizeNode = weightChild.getAttributeNodeNS(ANDROID_URI, dimension); - String size = sizeNode != null ? sizeNode.getValue() : "(undefined)"; - if (!size.startsWith("0")) { //$NON-NLS-1$ - String msg = String.format( - "Use a %1$s of 0dip instead of %2$s for better performance", - dimension, size); - context.report(INEFFICIENT_WEIGHT, - weightChild, - context.getLocation(sizeNode != null ? sizeNode : weightChild), msg, null); - - } - } - - if (context.isEnabled(WRONG_0DP)) { - checkWrong0Dp(context, element, children); - } - } - - private static void checkWrong0Dp(XmlContext context, Element element, - List<Element> children) { - boolean isVertical = false; - String orientation = element.getAttributeNS(ANDROID_URI, ATTR_ORIENTATION); - if (VALUE_VERTICAL.equals(orientation)) { - isVertical = true; - } - - for (Element child : children) { - String tagName = child.getTagName(); - if (tagName.equals(VIEW)) { - // Might just used for spacing - return; - } - if (tagName.indexOf('.') != -1 || tagName.equals(VIEW_TAG)) { - // Custom views might perform their own dynamic sizing or ignore the layout - // attributes all together - return; - } - - boolean hasWeight = child.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT); - - Attr widthNode = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH); - Attr heightNode = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT); - - boolean noWidth = false; - boolean noHeight = false; - if (widthNode != null && widthNode.getValue().startsWith("0")) { //$NON-NLS-1$ - noWidth = true; - } - if (heightNode != null && heightNode.getValue().startsWith("0")) { //$NON-NLS-1$ - noHeight = true; - } else if (!noWidth) { - return; - } - - // If you're specifying 0dp for both the width and height you are probably - // trying to hide it deliberately - if (noWidth && noHeight) { - return; - } - assert noWidth || noHeight; - - if (noWidth) { - assert widthNode != null; - if (!hasWeight) { - context.report(WRONG_0DP, widthNode, context.getLocation(widthNode), - "Suspicious size: this will make the view invisible, should be " + - "used with layout_weight", null); - } else if (isVertical) { - context.report(WRONG_0DP, widthNode, context.getLocation(widthNode), - "Suspicious size: this will make the view invisible, probably " + - "intended for layout_height", null); - } - } else { - assert noHeight; - assert heightNode != null; - if (!hasWeight) { - context.report(WRONG_0DP, widthNode, context.getLocation(heightNode), - "Suspicious size: this will make the view invisible, should be " + - "used with layout_weight", null); - } else if (!isVertical) { - context.report(WRONG_0DP, widthNode, context.getLocation(heightNode), - "Suspicious size: this will make the view invisible, probably " + - "intended for layout_width", null); - } - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/InvalidPackageDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/InvalidPackageDetector.java deleted file mode 100644 index 048f6ae..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/InvalidPackageDetector.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -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.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.Sets; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.LdcInsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; - -import java.io.File; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; - -import lombok.ast.libs.org.parboiled.google.collect.Lists; - -/** - * Looks for usages of Java packages that are not included in Android. - */ -public class InvalidPackageDetector extends Detector implements Detector.ClassScanner { - /** Accessing an invalid package */ - public static final Issue ISSUE = Issue.create("InvalidPackage", //$NON-NLS-1$ - "Finds API accesses to APIs that are not supported in Android", - - "This check scans through libraries looking for calls to APIs that are not included " + - "in Android.\n" + - "\n" + - "When you create Android projects, the classpath is set up such that you can only " + - "access classes in the API packages that are included in Android. However, if you " + - "add other projects to your libs/ folder, there is no guarantee that those .jar " + - "files were built with an Android specific classpath, and in particular, they " + - "could be accessing unsupported APIs such as java.applet.\n" + - "\n" + - "This check scans through library jars and looks for references to API packages " + - "that are not included in Android and flags these. This is only an error if your " + - "code calls one of the library classes which wind up referencing the unsupported " + - "package.", - - Category.CORRECTNESS, - 6, - Severity.ERROR, - InvalidPackageDetector.class, - EnumSet.of(Scope.JAVA_LIBRARIES)); - - private static final String JAVA_PKG_PREFIX = "java/"; //$NON-NLS-1$ - private static final String JAVAX_PKG_PREFIX = "javax/"; //$NON-NLS-1$ - - private ApiLookup mApiDatabase; - - /** - * List of candidates that are potential package violations. These are - * recorded as candidates rather than flagged immediately such that we can - * filter out hits for classes that are also defined as libraries (possibly - * encountered later in the library traversal). - */ - private List<Candidate> mCandidates; - /** - * Set of Java packages defined in the libraries; this means that if the - * user has added libraries in this package namespace (such as the - * null annotations jars) we don't flag these. - */ - private final Set<String> mJavaxLibraryClasses = Sets.newHashSetWithExpectedSize(64); - - /** Constructs a new package check */ - public InvalidPackageDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.SLOW; - } - - @Override - public void beforeCheckProject(@NonNull Context context) { - mApiDatabase = ApiLookup.get(context.getClient()); - } - - // ---- Implements ClassScanner ---- - - @SuppressWarnings("rawtypes") // ASM API - @Override - public void checkClass(@NonNull final ClassContext context, @NonNull ClassNode classNode) { - if (!context.isFromClassLibrary() || shouldSkip(context.file)) { - return; - } - - if (mApiDatabase == null) { - return; - } - - if (classNode.name.startsWith(JAVAX_PKG_PREFIX)) { - mJavaxLibraryClasses.add(classNode.name); - } - - List methodList = classNode.methods; - for (Object m : methodList) { - MethodNode method = (MethodNode) m; - - InsnList nodes = method.instructions; - - // Check return type - // The parameter types are already handled as local variables so we can skip - // right to the return type. - // Check types in parameter list - String signature = method.desc; - if (signature != null) { - int args = signature.indexOf(')'); - if (args != -1 && signature.charAt(args + 1) == 'L') { - String type = signature.substring(args + 2, signature.length() - 1); - if (isInvalidPackage(type)) { - AbstractInsnNode first = nodes.size() > 0 ? nodes.get(0) : null; - record(context, method, first, type); - } - } - } - - for (int i = 0, n = nodes.size(); i < n; i++) { - AbstractInsnNode instruction = nodes.get(i); - int type = instruction.getType(); - if (type == AbstractInsnNode.METHOD_INSN) { - MethodInsnNode node = (MethodInsnNode) instruction; - String owner = node.owner; - - // No need to check methods in this local class; we know they - // won't be an API match - if (node.getOpcode() == Opcodes.INVOKEVIRTUAL - && owner.equals(classNode.name)) { - owner = classNode.superName; - } - - while (owner != null) { - if (isInvalidPackage(owner)) { - record(context, method, instruction, owner); - } - - // For virtual dispatch, walk up the inheritance chain checking - // each inherited method - if (owner.startsWith("android/") //$NON-NLS-1$ - || owner.startsWith(JAVA_PKG_PREFIX) - || owner.startsWith(JAVAX_PKG_PREFIX)) { - owner = null; - } else if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) { - owner = context.getDriver().getSuperClass(owner); - } else if (node.getOpcode() == Opcodes.INVOKESTATIC) { - // Inherit through static classes as well - owner = context.getDriver().getSuperClass(owner); - } else { - owner = null; - } - } - } else if (type == AbstractInsnNode.FIELD_INSN) { - FieldInsnNode node = (FieldInsnNode) instruction; - String owner = node.owner; - if (isInvalidPackage(owner)) { - record(context, method, instruction, owner); - } - } else if (type == AbstractInsnNode.LDC_INSN) { - LdcInsnNode node = (LdcInsnNode) instruction; - if (node.cst instanceof Type) { - Type t = (Type) node.cst; - String className = t.getInternalName(); - if (isInvalidPackage(className)) { - record(context, method, instruction, className); - } - } - } - } - } - } - - private boolean isInvalidPackage(String owner) { - if (owner.startsWith(JAVA_PKG_PREFIX) - || owner.startsWith(JAVAX_PKG_PREFIX)) { - return !mApiDatabase.isValidJavaPackage(owner); - } - - return false; - } - - private void record(ClassContext context, MethodNode method, - AbstractInsnNode instruction, String owner) { - if (owner.indexOf('$') != -1) { - // Don't report inner classes too; there will pretty much always be an outer class - // reference as well - return; - } - - if (mCandidates == null) { - mCandidates = Lists.newArrayList(); - } - mCandidates.add(new Candidate(owner, context.getClassNode().name, context.getJarFile())); - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (mCandidates == null) { - return; - } - - for (Candidate candidate : mCandidates) { - String type = candidate.mClass; - if (mJavaxLibraryClasses.contains(type)) { - continue; - } - File jarFile = candidate.mJarFile; - String referencedIn = candidate.mReferencedIn; - - Location location = Location.create(jarFile); - Object pkg = getPackageName(type); - String message = String.format( - "Invalid package reference in library; not included in Android: %1$s. " + - "Referenced from %2$s.", pkg, ClassContext.getFqcn(referencedIn)); - context.report(ISSUE, location, message, null); - } - } - - private static Object getPackageName(String owner) { - String pkg = owner; - int index = pkg.lastIndexOf('/'); - if (index != -1) { - pkg = pkg.substring(0, index); - } - - return ClassContext.getFqcn(pkg); - } - - private static boolean shouldSkip(File file) { - // No need to do work on this library, which is included in pretty much all new ADT - // projects - if (file.getPath().endsWith("android-support-v4.jar")) { //$NON-NLS-1$ - return true; - } - - return false; - } - - private static class Candidate { - private final String mReferencedIn; - private final File mJarFile; - private final String mClass; - - public Candidate(String className, String referencedIn, File jarFile) { - mClass = className; - mReferencedIn = referencedIn; - mJarFile = jarFile; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java deleted file mode 100644 index b161699..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java +++ /dev/null @@ -1,565 +0,0 @@ -/* - * 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 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.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -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.Sets; -import com.google.common.collect.Sets.SetView; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.BinaryExpression; -import lombok.ast.BinaryOperator; -import lombok.ast.ConstructorInvocation; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.If; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.This; -import lombok.ast.Throw; -import lombok.ast.TypeReference; -import lombok.ast.TypeReferencePart; -import lombok.ast.UnaryExpression; -import lombok.ast.VariableDefinition; -import lombok.ast.VariableReference; - -/** - * Looks for performance issues in Java files, such as memory allocations during - * drawing operations and using HashMap instead of SparseArray. - */ -public class JavaPerformanceDetector extends Detector implements Detector.JavaScanner { - /** Allocating objects during a paint method */ - public static final Issue PAINT_ALLOC = Issue.create( - "DrawAllocation", //$NON-NLS-1$ - "Looks for memory allocations within drawing code", - - "You should avoid allocating objects during a drawing or layout operation. These " + - "are called frequently, so a smooth UI can be interrupted by garbage collection " + - "pauses caused by the object allocations.\n" + - "\n" + - "The way this is generally handled is to allocate the needed objects up front " + - "and to reuse them for each drawing operation.\n" + - "\n" + - "Some methods allocate memory on your behalf (such as `Bitmap.create`), and these " + - "should be handled in the same way.", - - Category.PERFORMANCE, - 9, - Severity.WARNING, - JavaPerformanceDetector.class, - Scope.JAVA_FILE_SCOPE); - - /** Using HashMaps where SparseArray would be better */ - public static final Issue USE_SPARSEARRAY = Issue.create( - "UseSparseArrays", //$NON-NLS-1$ - "Looks for opportunities to replace HashMaps with the more efficient SparseArray", - - "For maps where the keys are of type integer, it's typically more efficient to " + - "use the Android `SparseArray` API. This check identifies scenarios where you might " + - "want to consider using `SparseArray` instead of `HashMap` for better performance.\n" + - "\n" + - "This is *particularly* useful when the value types are primitives like ints, " + - "where you can use `SparseIntArray` and avoid auto-boxing the values from `int` to " + - "`Integer`.\n" + - "\n" + - "If you need to construct a `HashMap` because you need to call an API outside of " + - "your control which requires a `Map`, you can suppress this warning using for " + - "example the `@SuppressLint` annotation.", - - Category.PERFORMANCE, - 4, - Severity.WARNING, - JavaPerformanceDetector.class, - Scope.JAVA_FILE_SCOPE); - - /** Using {@code new Integer()} instead of the more efficient {@code Integer.valueOf} */ - public static final Issue USE_VALUEOF = Issue.create( - "UseValueOf", //$NON-NLS-1$ - "Looks for usages of \"new\" for wrapper classes which should use \"valueOf\" instead", - - "You should not call the constructor for wrapper classes directly, such as" + - "`new Integer(42)`. Instead, call the `valueOf` factory method, such as " + - "`Integer.valueOf(42)`. This will typically use less memory because common integers " + - "such as 0 and 1 will share a single instance.", - - Category.PERFORMANCE, - 4, - Severity.WARNING, - JavaPerformanceDetector.class, - Scope.JAVA_FILE_SCOPE); - - static final String ON_MEASURE = "onMeasure"; //$NON-NLS-1$ - static final String ON_DRAW = "onDraw"; //$NON-NLS-1$ - static final String ON_LAYOUT = "onLayout"; //$NON-NLS-1$ - private static final String INT = "int"; //$NON-NLS-1$ - private static final String INTEGER = "Integer"; //$NON-NLS-1$ - private static final String BOOL = "boolean"; //$NON-NLS-1$ - private static final String BOOLEAN = "Boolean"; //$NON-NLS-1$ - private static final String LONG = "Long"; //$NON-NLS-1$ - private static final String CHARACTER = "Character"; //$NON-NLS-1$ - private static final String DOUBLE = "Double"; //$NON-NLS-1$ - private static final String FLOAT = "Float"; //$NON-NLS-1$ - private static final String HASH_MAP = "HashMap"; //$NON-NLS-1$ - private static final String SPARSE_ARRAY = "SparseArray"; //$NON-NLS-1$ - private static final String CANVAS = "Canvas"; //$NON-NLS-1$ - private static final String LAYOUT = "layout"; //$NON-NLS-1$ - - /** Constructs a new {@link JavaPerformanceDetector} check */ - public JavaPerformanceDetector() { - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements JavaScanner ---- - - @Override - public List<Class<? extends Node>> getApplicableNodeTypes() { - List<Class<? extends Node>> types = new ArrayList<Class<? extends Node>>(3); - types.add(ConstructorInvocation.class); - types.add(MethodDeclaration.class); - types.add(MethodInvocation.class); - return types; - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - return new PerformanceVisitor(context); - } - - private static class PerformanceVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; - private final boolean mCheckMaps; - private final boolean mCheckAllocations; - private final boolean mCheckValueOf; - /** Whether allocations should be "flagged" in the current method */ - private boolean mFlagAllocations; - - public PerformanceVisitor(JavaContext context) { - mContext = context; - - mCheckAllocations = context.isEnabled(PAINT_ALLOC); - mCheckMaps = context.isEnabled(USE_SPARSEARRAY); - mCheckValueOf = context.isEnabled(USE_VALUEOF); - } - - @Override - public boolean visitMethodDeclaration(MethodDeclaration node) { - mFlagAllocations = isBlockedAllocationMethod(node); - - return super.visitMethodDeclaration(node); - } - - @Override - public boolean visitConstructorInvocation(ConstructorInvocation node) { - String typeName = null; - if (mCheckMaps) { - TypeReference reference = node.astTypeReference(); - typeName = reference.astParts().last().astIdentifier().astValue(); - // TODO: Should we handle factory method constructions of HashMaps as well, - // e.g. via Guava? This is a bit trickier since we need to infer the type - // arguments from the calling context. - if (typeName.equals(HASH_MAP)) { - checkHashMap(node, reference); - } else if (typeName.equals(SPARSE_ARRAY)) { - checkSparseArray(node, reference); - } - } - - if (mCheckValueOf) { - if (typeName == null) { - TypeReference reference = node.astTypeReference(); - typeName = reference.astParts().last().astIdentifier().astValue(); - } - if ((typeName.equals(INTEGER) - || typeName.equals(BOOLEAN) - || typeName.equals(FLOAT) - || typeName.equals(CHARACTER) - || typeName.equals(LONG) - || typeName.equals(DOUBLE)) - && node.astTypeReference().astParts().size() == 1 - && node.astArguments().size() == 1) { - String argument = node.astArguments().first().toString(); - mContext.report(USE_VALUEOF, node, mContext.getLocation(node), - String.format("Use %1$s.valueOf(%2$s) instead", typeName, argument), - null); - } - } - - if (mFlagAllocations && !(node.getParent() instanceof Throw) && mCheckAllocations) { - // Make sure we're still inside the method declaration that marked - // mInDraw as true, in case we've left it and we're in a static - // block or something: - Node method = node; - while (method != null) { - if (method instanceof MethodDeclaration) { - break; - } - method = method.getParent(); - } - if (method != null && isBlockedAllocationMethod(((MethodDeclaration) method)) - && !isLazilyInitialized(node)) { - reportAllocation(node); - } - } - - return super.visitConstructorInvocation(node); - } - - private void reportAllocation(Node node) { - mContext.report(PAINT_ALLOC, node, mContext.getLocation(node), - "Avoid object allocations during draw/layout operations (preallocate and " + - "reuse instead)", null); - } - - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (mFlagAllocations && node.astOperand() != null) { - // Look for forbidden methods - String methodName = node.astName().astValue(); - if (methodName.equals("createBitmap") //$NON-NLS-1$ - || methodName.equals("createScaledBitmap")) { //$NON-NLS-1$ - String operand = node.astOperand().toString(); - if (operand.equals("Bitmap") //$NON-NLS-1$ - || operand.equals("android.graphics.Bitmap")) { //$NON-NLS-1$ - if (!isLazilyInitialized(node)) { - reportAllocation(node); - } - } - } else if (methodName.startsWith("decode")) { //$NON-NLS-1$ - // decodeFile, decodeByteArray, ... - String operand = node.astOperand().toString(); - if (operand.equals("BitmapFactory") //$NON-NLS-1$ - || operand.equals("android.graphics.BitmapFactory")) { //$NON-NLS-1$ - if (!isLazilyInitialized(node)) { - reportAllocation(node); - } - } - } else if (methodName.equals("getClipBounds")) { //$NON-NLS-1$ - if (node.astArguments().isEmpty()) { - mContext.report(PAINT_ALLOC, node, mContext.getLocation(node), - "Avoid object allocations during draw operations: Use " + - "Canvas.getClipBounds(Rect) instead of Canvas.getClipBounds() " + - "which allocates a temporary Rect", null); - } - } - } - - return super.visitMethodInvocation(node); - } - - /** - * Check whether the given invocation is done as a lazy initialization, - * e.g. {@code if (foo == null) foo = new Foo();}. - * <p> - * This tries to also handle the scenario where the check is on some - * <b>other</b> variable - e.g. - * <pre> - * if (foo == null) { - * foo == init1(); - * bar = new Bar(); - * } - * </pre> - * or - * <pre> - * if (!initialized) { - * initialized = true; - * bar = new Bar(); - * } - * </pre> - */ - private static boolean isLazilyInitialized(Node node) { - Node curr = node.getParent(); - while (curr != null) { - if (curr instanceof MethodDeclaration) { - return false; - } else if (curr instanceof If) { - If ifNode = (If) curr; - // See if the if block represents a lazy initialization: - // compute all variable names seen in the condition - // (e.g. for "if (foo == null || bar != foo)" the result is "foo,bar"), - // and then compute all variables assigned to in the if body, - // and if there is an overlap, we'll consider the whole if block - // guarded (so lazily initialized and an allocation we won't complain - // about.) - List<String> assignments = new ArrayList<String>(); - AssignmentTracker visitor = new AssignmentTracker(assignments); - ifNode.astStatement().accept(visitor); - if (!assignments.isEmpty()) { - List<String> references = new ArrayList<String>(); - addReferencedVariables(references, ifNode.astCondition()); - if (!references.isEmpty()) { - SetView<String> intersection = Sets.intersection( - new HashSet<String>(assignments), - new HashSet<String>(references)); - return !intersection.isEmpty(); - } - } - return false; - - } - curr = curr.getParent(); - } - - return false; - } - - /** Adds any variables referenced in the given expression into the given list */ - private static void addReferencedVariables(Collection<String> variables, - Expression expression) { - if (expression instanceof BinaryExpression) { - BinaryExpression binary = (BinaryExpression) expression; - addReferencedVariables(variables, binary.astLeft()); - addReferencedVariables(variables, binary.astRight()); - } else if (expression instanceof UnaryExpression) { - UnaryExpression unary = (UnaryExpression) expression; - addReferencedVariables(variables, unary.astOperand()); - } else if (expression instanceof VariableReference) { - VariableReference reference = (VariableReference) expression; - variables.add(reference.astIdentifier().astValue()); - } else if (expression instanceof Select) { - Select select = (Select) expression; - if (select.astOperand() instanceof This) { - variables.add(select.astIdentifier().astValue()); - } - } - } - - /** - * Returns whether the given method declaration represents a method - * where allocating objects is not allowed for performance reasons - */ - private static boolean isBlockedAllocationMethod(MethodDeclaration node) { - return isOnDrawMethod(node) || isOnMeasureMethod(node) || isOnLayoutMethod(node) - || isLayoutMethod(node); - } - - /** - * Returns true if this method looks like it's overriding android.view.View's - * {@code protected void onDraw(Canvas canvas)} - */ - private static boolean isOnDrawMethod(MethodDeclaration node) { - if (ON_DRAW.equals(node.astMethodName().astValue())) { - StrictListAccessor<VariableDefinition, MethodDeclaration> parameters = - node.astParameters(); - if (parameters != null && parameters.size() == 1) { - VariableDefinition arg0 = parameters.first(); - TypeReferencePart type = arg0.astTypeReference().astParts().last(); - String typeName = type.getTypeName(); - if (typeName.equals(CANVAS)) { - return true; - } - } - } - - return false; - } - - /** - * Returns true if this method looks like it's overriding - * android.view.View's - * {@code protected void onLayout(boolean changed, int left, int top, - * int right, int bottom)} - */ - private static boolean isOnLayoutMethod(MethodDeclaration node) { - if (ON_LAYOUT.equals(node.astMethodName().astValue())) { - StrictListAccessor<VariableDefinition, MethodDeclaration> parameters = - node.astParameters(); - if (parameters != null && parameters.size() == 5) { - Iterator<VariableDefinition> iterator = parameters.iterator(); - if (!iterator.hasNext()) { - return false; - } - - // Ensure that the argument list matches boolean, int, int, int, int - TypeReferencePart type = iterator.next().astTypeReference().astParts().last(); - if (!type.getTypeName().equals(BOOL) || !iterator.hasNext()) { - return false; - } - for (int i = 0; i < 4; i++) { - type = iterator.next().astTypeReference().astParts().last(); - if (!type.getTypeName().equals(INT)) { - return false; - } - if (!iterator.hasNext()) { - return i == 3; - } - } - } - } - - return false; - } - - /** - * Returns true if this method looks like it's overriding android.view.View's - * {@code protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)} - */ - private static boolean isOnMeasureMethod(MethodDeclaration node) { - if (ON_MEASURE.equals(node.astMethodName().astValue())) { - StrictListAccessor<VariableDefinition, MethodDeclaration> parameters = - node.astParameters(); - if (parameters != null && parameters.size() == 2) { - VariableDefinition arg0 = parameters.first(); - VariableDefinition arg1 = parameters.last(); - TypeReferencePart type1 = arg0.astTypeReference().astParts().last(); - TypeReferencePart type2 = arg1.astTypeReference().astParts().last(); - return INT.equals(type1.getTypeName()) && INT.equals(type2.getTypeName()); - } - } - - return false; - } - - /** - * Returns true if this method looks like it's overriding android.view.View's - * {@code public void layout(int l, int t, int r, int b)} - */ - private static boolean isLayoutMethod(MethodDeclaration node) { - if (LAYOUT.equals(node.astMethodName().astValue())) { - StrictListAccessor<VariableDefinition, MethodDeclaration> parameters = - node.astParameters(); - if (parameters != null && parameters.size() == 4) { - Iterator<VariableDefinition> iterator = parameters.iterator(); - for (int i = 0; i < 4; i++) { - if (!iterator.hasNext()) { - return false; - } - VariableDefinition next = iterator.next(); - TypeReferencePart type = next.astTypeReference().astParts().last(); - if (!INT.equals(type.getTypeName())) { - return false; - } - } - return true; - } - } - - return false; - } - - - /** - * Checks whether the given constructor call and type reference refers - * to a HashMap constructor call that is eligible for replacement by a - * SparseArray call instead - */ - private void checkHashMap(ConstructorInvocation node, TypeReference reference) { - // reference.hasTypeArguments returns false where it should not - StrictListAccessor<TypeReference, TypeReference> types = reference.getTypeArguments(); - if (types != null && types.size() == 2) { - TypeReference first = types.first(); - if (first.getTypeName().equals(INTEGER)) { - String valueType = types.last().getTypeName(); - if (valueType.equals(INTEGER)) { - mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node), - "Use new SparseIntArray(...) instead for better performance", - null); - } else if (valueType.equals(BOOLEAN)) { - mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node), - "Use new SparseBooleanArray(...) instead for better performance", - null); - } else if (valueType.equals(LONG) && mContext.getProject().getMinSdk() >= 17) { - mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node), - "Use new SparseLongArray(...) instead for better performance", - null); - } else { - mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node), - String.format( - "Use new SparseArray<%1$s>(...) instead for better performance", - valueType), - null); - } - } - } - } - - private void checkSparseArray(ConstructorInvocation node, TypeReference reference) { - // reference.hasTypeArguments returns false where it should not - StrictListAccessor<TypeReference, TypeReference> types = reference.getTypeArguments(); - if (types != null && types.size() == 1) { - TypeReference first = types.first(); - String valueType = first.getTypeName(); - if (valueType.equals(INTEGER)) { - mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node), - "Use new SparseIntArray(...) instead for better performance", - null); - } else if (valueType.equals(BOOLEAN)) { - mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node), - "Use new SparseBooleanArray(...) instead for better performance", - null); - } else if (valueType.equals(LONG) && mContext.getProject().getMinSdk() >= 17) { - mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node), - "Use new SparseLongArray(...) instead for better performance", - null); - } - } - } - } - - /** Visitor which records variable names assigned into */ - private static class AssignmentTracker extends ForwardingAstVisitor { - private final Collection<String> mVariables; - - public AssignmentTracker(Collection<String> variables) { - mVariables = variables; - } - - @Override - public boolean visitBinaryExpression(BinaryExpression node) { - BinaryOperator operator = node.astOperator(); - if (operator == BinaryOperator.ASSIGN || operator == BinaryOperator.OR_ASSIGN) { - Expression left = node.astLeft(); - String variable; - if (left instanceof Select && ((Select) left).astOperand() instanceof This) { - variable = ((Select) left).astIdentifier().astValue(); - } else { - variable = left.toString(); - } - mVariables.add(variable); - } - - return super.visitBinaryExpression(node); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/LabelForDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/LabelForDetector.java deleted file mode 100644 index 283e244..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/LabelForDetector.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * 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.ANDROID_URI; -import static com.android.SdkConstants.ATTR_HINT; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_LABEL_FOR; -import static com.android.SdkConstants.AUTO_COMPLETE_TEXT_VIEW; -import static com.android.SdkConstants.EDIT_TEXT; -import static com.android.SdkConstants.ID_PREFIX; -import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW; -import static com.android.SdkConstants.NEW_ID_PREFIX; -import static com.android.tools.lint.detector.api.LintUtils.stripIdPrefix; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -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.android.tools.lint.detector.api.XmlContext; -import com.google.common.collect.Sets; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -/** - * Detector which finds unlabeled text fields - */ -public class LabelForDetector extends LayoutDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "LabelFor", //$NON-NLS-1$ - "Ensures that text fields are marked with a labelFor attribute", - "Text fields should be labelled with a `labelFor` attribute, " + - "provided your `minSdkVersion` is at least 17.\n" + - "\n" + - "If your view is labeled but by a label in a different layout which " + - "includes this one, just suppress this warning from lint.", - Category.A11Y, - 2, - Severity.WARNING, - LabelForDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - private Set<String> mLabels; - private List<Element> mTextFields; - - /** Constructs a new {@link LabelForDetector} */ - public LabelForDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - @Nullable - public Collection<String> getApplicableAttributes() { - return Collections.singletonList(ATTR_LABEL_FOR); - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - EDIT_TEXT, - AUTO_COMPLETE_TEXT_VIEW, - MULTI_AUTO_COMPLETE_TEXT_VIEW - ); - } - - @Override - public void afterCheckFile(@NonNull Context context) { - if (mTextFields != null) { - if (mLabels == null) { - mLabels = Collections.emptySet(); - } - - for (Element element : mTextFields) { - if (element.hasAttributeNS(ANDROID_URI, ATTR_HINT)) { - continue; - } - String id = element.getAttributeNS(ANDROID_URI, ATTR_ID); - boolean missing = true; - if (mLabels.contains(id)) { - missing = false; - } else if (id.startsWith(NEW_ID_PREFIX)) { - missing = !mLabels.contains(ID_PREFIX + stripIdPrefix(id)); - } else if (id.startsWith(ID_PREFIX)) { - missing = !mLabels.contains(NEW_ID_PREFIX + stripIdPrefix(id)); - } - - if (missing) { - XmlContext xmlContext = (XmlContext) context; - Location location = xmlContext.getLocation(element); - String message; - if (id == null || id.isEmpty()) { - message = "No label views point to this text field with a " + - "labelFor attribute"; - } else { - message = String.format("No label views point to this text field with " + - "an android:labelFor=\"@+id/%1$s\" attribute", id); - } - xmlContext.report(ISSUE, element, location, message, null); - } - - } - } - - mLabels = null; - mTextFields = null; - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - if (mLabels == null) { - mLabels = Sets.newHashSet(); - } - mLabels.add(attribute.getValue()); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - // NOTE: This should NOT be checking *minSdkVersion*, but *targetSdkVersion* - // or even buildTarget instead. However, there's a risk that this will flag - // way too much and make the rule annoying until API 17 support becomes - // more widespread, so for now limit the check to those projects *really* - // working with 17. When API 17 reaches a certain amount of adoption, change - // this to flag all apps supporting 17, including those supporting earlier - // versions as well. - if (context.getMainProject().getMinSdk() < 17) { - return; - } - - if (mTextFields == null) { - mTextFields = new ArrayList<Element>(); - } - mTextFields.add(element); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/LocaleDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/LocaleDetector.java deleted file mode 100644 index 3bde211..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/LocaleDetector.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * 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 com.android.SdkConstants.FORMAT_METHOD; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -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 org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.LdcInsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.analysis.Analyzer; -import org.objectweb.asm.tree.analysis.AnalyzerException; -import org.objectweb.asm.tree.analysis.Frame; -import org.objectweb.asm.tree.analysis.SourceInterpreter; -import org.objectweb.asm.tree.analysis.SourceValue; - -import java.util.Arrays; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; - -/** - * Checks for errors related to locale handling - */ -public class LocaleDetector extends Detector implements ClassScanner { - /** Calling risky convenience methods */ - public static final Issue STRING_LOCALE = Issue.create( - "DefaultLocale", //$NON-NLS-1$ - "Finds calls to locale-ambiguous String manipulation methods", - - "Calling `String#toLowerCase()` or `#toUpperCase()` *without specifying an " + - "explicit locale* is a common source of bugs. The reason for that is that those " + - "methods will use the current locale on the user's device, and even though the " + - "code appears to work correctly when you are developing the app, it will fail " + - "in some locales. For example, in the Turkish locale, the uppercase replacement " + - "for `i` is *not* `I`.\n" + - "\n" + - "If you want the methods to just perform ASCII replacement, for example to convert " + - "an enum name, call `String#toUpperCase(Locale.US)` instead. If you really want to " + - "use the current locale, call `String#toUpperCase(Locale.getDefault())` instead.", - - Category.CORRECTNESS, - 6, - Severity.WARNING, - LocaleDetector.class, - EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.CLASS_FILE)).setMoreInfo( - "http://developer.android.com/reference/java/util/Locale.html#default_locale"); //$NON-NLS-1$ - - /** Constructing SimpleDateFormat without an explicit locale */ - public static final Issue DATE_FORMAT = Issue.create( - "SimpleDateFormat", //$NON-NLS-1$ - "Using SimpleDateFormat directly without an explicit locale", - - "Almost all callers should use `getDateInstance()`, `getDateTimeInstance()`, or " + - "`getTimeInstance()` to get a ready-made instance of SimpleDateFormat suitable " + - "for the user's locale. The main reason you'd create an instance this class " + - "directly is because you need to format/parse a specific machine-readable format, " + - "in which case you almost certainly want to explicitly ask for US to ensure that " + - "you get ASCII digits (rather than, say, Arabic digits).\n" + - "\n" + - "Therefore, you should either use the form of the SimpleDateFormat constructor " + - "where you pass in an explicit locale, such as Locale.US, or use one of the " + - "get instance methods, or suppress this error if really know what you are doing.", - - Category.CORRECTNESS, - 6, - Severity.WARNING, - LocaleDetector.class, - Scope.CLASS_FILE_SCOPE).setMoreInfo( - "http://developer.android.com/reference/java/text/SimpleDateFormat.html"); //$NON-NLS-1$ - - static final String DATE_FORMAT_OWNER = "java/text/SimpleDateFormat"; //$NON-NLS-1$ - private static final String STRING_OWNER = "java/lang/String"; //$NON-NLS-1$ - - /** Constructs a new {@link LocaleDetector} */ - public LocaleDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements ClassScanner ---- - - @Override - @Nullable - public List<String> getApplicableCallNames() { - return Arrays.asList( - "toLowerCase", //$NON-NLS-1$ - "toUpperCase", //$NON-NLS-1$ - FORMAT_METHOD - ); - } - - @Override - @Nullable - public List<String> getApplicableCallOwners() { - return Collections.singletonList(DATE_FORMAT_OWNER); - } - - @Override - public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode call) { - String owner = call.owner; - String desc = call.desc; - String name = call.name; - if (owner.equals(DATE_FORMAT_OWNER)) { - if (!name.equals(CONSTRUCTOR_NAME)) { - return; - } - if (desc.equals("(Ljava/lang/String;Ljava/text/DateFormatSymbols;)V") //$NON-NLS-1$ - || desc.equals("()V") //$NON-NLS-1$ - || desc.equals("(Ljava/lang/String;)V")) { //$NON-NLS-1$ - Location location = context.getLocation(call); - String message = - "To get local formatting use getDateInstance(), getDateTimeInstance(), " + - "or getTimeInstance(), or use new SimpleDateFormat(String template, " + - "Locale locale) with for example Locale.US for ASCII dates."; - context.report(DATE_FORMAT, method, call, location, message, null); - } - return; - } else if (!owner.equals(STRING_OWNER)) { - return; - } - - if (name.equals(FORMAT_METHOD)) { - // Only check the non-locale version of String.format - if (!desc.equals("(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;")) { //$NON-NLS-1$ - return; - } - // Find the formatting string - Analyzer analyzer = new Analyzer(new SourceInterpreter() { - @Override - public SourceValue newOperation(AbstractInsnNode insn) { - if (insn.getOpcode() == Opcodes.LDC) { - Object cst = ((LdcInsnNode) insn).cst; - if (cst instanceof String) { - return new StringValue(1, (String) cst); - } - } - return super.newOperation(insn); - } - }); - try { - Frame[] frames = analyzer.analyze(classNode.name, method); - InsnList instructions = method.instructions; - Frame frame = frames[instructions.indexOf(call)]; - if (frame.getStackSize() == 0) { - return; - } - SourceValue stackValue = (SourceValue) frame.getStack(0); - if (stackValue instanceof StringValue) { - String format = ((StringValue) stackValue).getString(); - if (format != null && StringFormatDetector.isLocaleSpecific(format)) { - Location location = context.getLocation(call); - String message = - "Implicitly using the default locale is a common source of bugs: " + - "Use String.format(Locale, ...) instead"; - context.report(STRING_LOCALE, method, call, location, message, null); - } - } - } catch (AnalyzerException e) { - context.log(e, null); - } - } else { - if (desc.equals("()Ljava/lang/String;")) { //$NON-NLS-1$ - Location location = context.getLocation(call); - String message = String.format( - "Implicitly using the default locale is a common source of bugs: " + - "Use %1$s(Locale) instead", name); - context.report(STRING_LOCALE, method, call, location, message, null); - } - } - } - - private static class StringValue extends SourceValue { - private final String mString; - - StringValue(int size, String string) { - super(size); - mString = string; - } - - String getString() { - return mString; - } - - @Override - public int getSize() { - return 1; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ManifestOrderDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ManifestOrderDetector.java deleted file mode 100644 index 83fac97..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ManifestOrderDetector.java +++ /dev/null @@ -1,557 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_MIN_SDK_VERSION; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_PACKAGE; -import static com.android.SdkConstants.ATTR_TARGET_SDK_VERSION; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; -import static com.android.SdkConstants.TAG_ACTIVITY; -import static com.android.SdkConstants.TAG_APPLICATION; -import static com.android.SdkConstants.TAG_PERMISSION; -import static com.android.SdkConstants.TAG_PROVIDER; -import static com.android.SdkConstants.TAG_RECEIVER; -import static com.android.SdkConstants.TAG_SERVICE; -import static com.android.SdkConstants.TAG_USES_LIBRARY; -import static com.android.SdkConstants.TAG_USES_PERMISSION; -import static com.android.SdkConstants.TAG_USES_SDK; - -import com.android.SdkConstants; -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.Detector; -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.android.tools.lint.detector.api.XmlContext; -import com.google.common.collect.Maps; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Checks for issues in AndroidManifest files such as declaring elements in the - * wrong order. - */ -public class ManifestOrderDetector extends Detector implements Detector.XmlScanner { - - /** Wrong order of elements in the manifest */ - public static final Issue ORDER = Issue.create( - "ManifestOrder", //$NON-NLS-1$ - "Checks for manifest problems like <uses-sdk> after the <application> tag", - "The <application> tag should appear after the elements which declare " + - "which version you need, which features you need, which libraries you " + - "need, and so on. In the past there have been subtle bugs (such as " + - "themes not getting applied correctly) when the `<application>` tag appears " + - "before some of these other elements, so it's best to order your " + - "manifest in the logical dependency order.", - Category.CORRECTNESS, - 5, - Severity.WARNING, - ManifestOrderDetector.class, - Scope.MANIFEST_SCOPE); - - /** Missing a {@code <uses-sdk>} element */ - public static final Issue USES_SDK = Issue.create( - "UsesMinSdkAttributes", //$NON-NLS-1$ - "Checks that the minimum SDK and target SDK attributes are defined", - - "The manifest should contain a `<uses-sdk>` element which defines the " + - "minimum API Level required for the application to run, " + - "as well as the target version (the highest API level you have tested " + - "the version for.)", - - Category.CORRECTNESS, - 9, - Severity.WARNING, - ManifestOrderDetector.class, - Scope.MANIFEST_SCOPE).setMoreInfo( - "http://developer.android.com/guide/topics/manifest/uses-sdk-element.html"); //$NON-NLS-1$ - - /** Using a targetSdkVersion that isn't recent */ - public static final Issue TARGET_NEWER = Issue.create( - "OldTargetApi", //$NON-NLS-1$ - "Checks that the manifest specifies a targetSdkVersion that is recent", - - "When your application runs on a version of Android that is more recent than your " + - "`targetSdkVersion` specifies that it has been tested with, various compatibility " + - "modes kick in. This ensures that your application continues to work, but it may " + - "look out of place. For example, if the `targetSdkVersion` is less than 14, your " + - "app may get an option button in the UI.\n" + - "\n" + - "To fix this issue, set the `targetSdkVersion` to the highest available value. Then " + - "test your app to make sure everything works correctly. You may want to consult " + - "the compatibility notes to see what changes apply to each version you are adding " + - "support for: " + - "http://developer.android.com/reference/android/os/Build.VERSION_CODES.html", - - Category.CORRECTNESS, - 6, - Severity.WARNING, - ManifestOrderDetector.class, - Scope.MANIFEST_SCOPE).setMoreInfo( - "http://developer.android.com/reference/android/os/Build.VERSION_CODES.html"); //$NON-NLS-1$ - - /** Using multiple {@code <uses-sdk>} elements */ - public static final Issue MULTIPLE_USES_SDK = Issue.create( - "MultipleUsesSdk", //$NON-NLS-1$ - "Checks that the <uses-sdk> element appears at most once", - - "The `<uses-sdk>` element should appear just once; the tools will *not* merge the " + - "contents of all the elements so if you split up the attributes across multiple " + - "elements, only one of them will take effect. To fix this, just merge all the " + - "attributes from the various elements into a single <uses-sdk> element.", - - Category.CORRECTNESS, - 6, - Severity.FATAL, - ManifestOrderDetector.class, - Scope.MANIFEST_SCOPE).setMoreInfo( - "http://developer.android.com/guide/topics/manifest/uses-sdk-element.html"); //$NON-NLS-1$ - - /** Missing a {@code <uses-sdk>} element */ - public static final Issue WRONG_PARENT = Issue.create( - "WrongManifestParent", //$NON-NLS-1$ - "Checks that various manifest elements are declared in the right place", - - "The `<uses-library>` element should be defined as a direct child of the " + - "`<application>` tag, not the `<manifest>` tag or an `<activity>` tag. Similarly, " + - "a `<uses-sdk>` tag much be declared at the root level, and so on. This check " + - "looks for incorrect declaration locations in the manifest, and complains " + - "if an element is found in the wrong place.", - - Category.CORRECTNESS, - 6, - Severity.FATAL, - ManifestOrderDetector.class, - Scope.MANIFEST_SCOPE).setMoreInfo( - "http://developer.android.com/guide/topics/manifest/manifest-intro.html"); //$NON-NLS-1$ - - /** Missing a {@code <uses-sdk>} element */ - public static final Issue DUPLICATE_ACTIVITY = Issue.create( - "DuplicateActivity", //$NON-NLS-1$ - "Checks that an activity is registered only once in the manifest", - - "An activity should only be registered once in the manifest. If it is " + - "accidentally registered more than once, then subtle errors can occur, " + - "since attribute declarations from the two elements are not merged, so " + - "you may accidentally remove previous declarations.", - - Category.CORRECTNESS, - 5, - Severity.ERROR, - ManifestOrderDetector.class, - Scope.MANIFEST_SCOPE); - - /** Not explicitly defining allowBackup */ - public static final Issue ALLOW_BACKUP = Issue.create( - "AllowBackup", //$NON-NLS-1$ - "Ensure that allowBackup is explicitly set in the application's manifest", - - "The allowBackup attribute determines if an application's data can be backed up " + - "and restored. It is documented at " + - "http://developer.android.com/reference/android/R.attr.html#allowBackup\n" + - "\n" + - "By default, this flag is set to `true`. When this flag is set to `true`, " + - "application data can be backed up and restored by the user using `adb backup` " + - "and `adb restore`.\n" + - "\n" + - "This may have security consequences for an application. `adb backup` allows " + - "users who have enabled USB debugging to copy application data off of the " + - "device. Once backed up, all application data can be read by the user. " + - "`adb restore` allows creation of application data from a source specified " + - "by the user. Following a restore, applications should not assume that the " + - "data, file permissions, and directory permissions were created by the " + - "application itself.\n" + - "\n" + - "Setting `allowBackup=\"false\"` opts an application out of both backup and " + - "restore.\n" + - "\n" + - "To fix this warning, decide whether your application should support backup, " + - "and explicitly set `android:allowBackup=(true|false)\"`", - - Category.SECURITY, - 3, - Severity.WARNING, - ManifestOrderDetector.class, - Scope.MANIFEST_SCOPE).setMoreInfo( - "http://developer.android.com/reference/android/R.attr.html#allowBackup"); - - /** Conflicting permission names */ - public static final Issue UNIQUE_PERMISSION = Issue.create( - "UniquePermission", //$NON-NLS-1$ - "Checks that permission names are unique", - - "The unqualified names or your permissions must be unique. The reason for this " + - "is that at build time, the `aapt` tool will generate a class named `Manifest` " + - "which contains a field for each of your permissions. These fields are named " + - "using your permission unqualified names (i.e. the name portion after the last " + - "dot).\n" + - "\n" + - "If more than one permission maps to the same field name, that field will " + - "arbitrarily name just one of them.", - - Category.CORRECTNESS, - 6, - Severity.ERROR, - ManifestOrderDetector.class, - Scope.MANIFEST_SCOPE); - - /** Using a resource for attributes that do not allow it */ - public static final Issue SET_VERSION = Issue.create( - "MissingVersion", //$NON-NLS-1$ - "Checks that the application name and version are set", - - "You should define the version information for your application.\n" + - "`android:versionCode`: An integer value that represents the version of the " + - "application code, relative to other versions.\n" + - "\n" + - "`android:versionName`: A string value that represents the release version of " + - "the application code, as it should be shown to users.", - - Category.CORRECTNESS, - 2, - Severity.WARNING, - ManifestOrderDetector.class, - Scope.MANIFEST_SCOPE).setMoreInfo( - "http://developer.android.com/tools/publishing/versioning.html#appversioning"); - - /** Using a resource for attributes that do not allow it */ - public static final Issue ILLEGAL_REFERENCE = Issue.create( - "IllegalResourceRef", //$NON-NLS-1$ - "Checks for resource references where only literals are allowed", - - "For the `versionCode` attribute, you have to specify an actual integer " + - "literal; you cannot use an indirection with a `@dimen/name` resource. " + - "Similarly, the `versionName` attribute should be an actual string, not " + - "a string resource url.", - - Category.CORRECTNESS, - 8, - Severity.WARNING, - ManifestOrderDetector.class, - Scope.MANIFEST_SCOPE); - - /** Constructs a new {@link ManifestOrderDetector} check */ - public ManifestOrderDetector() { - } - - private boolean mSeenApplication; - - /** Number of times we've seen the <uses-sdk> element */ - private int mSeenUsesSdk; - - /** Activities we've encountered */ - private final Set<String> mActivities = new HashSet<String>(); - - /** Permission basenames */ - private Map<String, String> mPermissionNames; - - /** Package declared in the manifest */ - private String mPackage; - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return file.getName().equals(ANDROID_MANIFEST_XML); - } - - @Override - public void beforeCheckFile(@NonNull Context context) { - mSeenApplication = false; - mSeenUsesSdk = 0; - } - - @Override - public void afterCheckFile(@NonNull Context context) { - XmlContext xmlContext = (XmlContext) context; - Element element = xmlContext.document.getDocumentElement(); - if (element != null) { - checkDocumentElement(xmlContext, element); - } - - if (mSeenUsesSdk == 0 && context.isEnabled(USES_SDK)) { - context.report(USES_SDK, Location.create(context.file), - "Manifest should specify a minimum API level with " + - "<uses-sdk android:minSdkVersion=\"?\" />; if it really supports " + - "all versions of Android set it to 1.", null); - } - } - - private void checkDocumentElement(XmlContext context, Element element) { - Attr codeNode = element.getAttributeNodeNS(ANDROID_URI, "versionCode");//$NON-NLS-1$ - if (codeNode != null && codeNode.getValue().startsWith(PREFIX_RESOURCE_REF) - && context.isEnabled(ILLEGAL_REFERENCE)) { - context.report(ILLEGAL_REFERENCE, element, context.getLocation(element), - "The android:versionCode cannot be a resource url, it must be " - + "a literal integer", null); - } else if (codeNode == null && context.isEnabled(SET_VERSION)) { - context.report(SET_VERSION, element, context.getLocation(element), - "Should set android:versionCode to specify the application version", null); - } - Attr nameNode = element.getAttributeNodeNS(ANDROID_URI, "versionName");//$NON-NLS-1$ - if (nameNode != null && nameNode.getValue().startsWith(PREFIX_RESOURCE_REF) - && context.isEnabled(ILLEGAL_REFERENCE)) { - context.report(ILLEGAL_REFERENCE, element, context.getLocation(element), - "The android:versionName cannot be a resource url, it must be " - + "a literal string", null); - } else if (nameNode == null && context.isEnabled(SET_VERSION)) { - context.report(SET_VERSION, element, context.getLocation(element), - "Should set android:versionName to specify the application version", null); - } - } - - // ---- Implements Detector.XmlScanner ---- - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - TAG_APPLICATION, - TAG_USES_PERMISSION, - TAG_PERMISSION, - "permission-tree", //$NON-NLS-1$ - "permission-group", //$NON-NLS-1$ - TAG_USES_SDK, - "uses-configuration", //$NON-NLS-1$ - "uses-feature", //$NON-NLS-1$ - "supports-screens", //$NON-NLS-1$ - "compatible-screens", //$NON-NLS-1$ - "supports-gl-texture", //$NON-NLS-1$ - TAG_USES_LIBRARY, - TAG_ACTIVITY, - TAG_SERVICE, - TAG_PROVIDER, - TAG_RECEIVER - ); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String tag = element.getTagName(); - Node parentNode = element.getParentNode(); - - if (tag.equals(TAG_USES_LIBRARY) || tag.equals(TAG_ACTIVITY) || tag.equals(TAG_SERVICE) - || tag.equals(TAG_PROVIDER) || tag.equals(TAG_RECEIVER)) { - if (!TAG_APPLICATION.equals(parentNode.getNodeName()) - && context.isEnabled(WRONG_PARENT)) { - context.report(WRONG_PARENT, element, context.getLocation(element), - String.format( - "The <%1$s> element must be a direct child of the <application> element", - tag), null); - } - - if (tag.equals(TAG_ACTIVITY)) { - Attr nameNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME); - if (nameNode != null) { - String name = nameNode.getValue(); - if (!name.isEmpty()) { - if (name.charAt(0) == '.') { - name = getPackage(element) + name; - } else if (name.indexOf('.') == -1) { - name = getPackage(element) + '.' + name; - } - if (mActivities.contains(name)) { - String message = String.format( - "Duplicate registration for activity %1$s", name); - context.report(DUPLICATE_ACTIVITY, element, - context.getLocation(nameNode), message, null); - } else { - mActivities.add(name); - } - } - } - } - - return; - } - - if (parentNode != element.getOwnerDocument().getDocumentElement() - && context.isEnabled(WRONG_PARENT)) { - context.report(WRONG_PARENT, element, context.getLocation(element), - String.format( - "The <%1$s> element must be a direct child of the " + - "<manifest> root element", tag), null); - } - - if (tag.equals(TAG_USES_SDK)) { - mSeenUsesSdk++; - - if (mSeenUsesSdk == 2) { // Only warn when we encounter the first one - Location location = context.getLocation(element); - - // Link up *all* encountered locations in the document - NodeList elements = element.getOwnerDocument().getElementsByTagName(TAG_USES_SDK); - Location secondary = null; - for (int i = elements.getLength() - 1; i >= 0; i--) { - Element e = (Element) elements.item(i); - if (e != element) { - Location l = context.getLocation(e); - l.setSecondary(secondary); - l.setMessage("Also appears here"); - secondary = l; - } - } - location.setSecondary(secondary); - - if (context.isEnabled(MULTIPLE_USES_SDK)) { - context.report(MULTIPLE_USES_SDK, element, location, - "There should only be a single <uses-sdk> element in the manifest:" + - " merge these together", null); - } - return; - } - - if (!element.hasAttributeNS(ANDROID_URI, ATTR_MIN_SDK_VERSION)) { - if (context.isEnabled(USES_SDK)) { - context.report(USES_SDK, element, context.getLocation(element), - "<uses-sdk> tag should specify a minimum API level with " + - "android:minSdkVersion=\"?\"", null); - } - } else { - Attr codeNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_MIN_SDK_VERSION); - if (codeNode != null && codeNode.getValue().startsWith(PREFIX_RESOURCE_REF) - && context.isEnabled(ILLEGAL_REFERENCE)) { - context.report(ILLEGAL_REFERENCE, element, context.getLocation(element), - "The android:minSdkVersion cannot be a resource url, it must be " - + "a literal integer (or string if a preview codename)", null); - } - } - - if (!element.hasAttributeNS(ANDROID_URI, ATTR_TARGET_SDK_VERSION)) { - // Warn if not setting target SDK -- but only if the min SDK is somewhat - // old so there's some compatibility stuff kicking in (such as the menu - // button etc) - if (context.isEnabled(USES_SDK)) { - context.report(USES_SDK, element, context.getLocation(element), - "<uses-sdk> tag should specify a target API level (the " + - "highest verified version; when running on later versions, " + - "compatibility behaviors may be enabled) with " + - "android:targetSdkVersion=\"?\"", null); - } - } else if (context.isEnabled(TARGET_NEWER)){ - String target = element.getAttributeNS(ANDROID_URI, ATTR_TARGET_SDK_VERSION); - try { - int api = Integer.parseInt(target); - if (api < context.getClient().getHighestKnownApiLevel()) { - context.report(TARGET_NEWER, element, context.getLocation(element), - "Not targeting the latest versions of Android; compatibility " + - "modes apply. Consider testing and updating this version. " + - "Consult the android.os.Build.VERSION_CODES javadoc for details.", - null); - } - } catch (NumberFormatException nufe) { - // Ignore: AAPT will enforce this. - } - } - - Attr nameNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_TARGET_SDK_VERSION); - if (nameNode != null && nameNode.getValue().startsWith(PREFIX_RESOURCE_REF) - && context.isEnabled(ILLEGAL_REFERENCE)) { - context.report(ILLEGAL_REFERENCE, element, context.getLocation(element), - "The android:targetSdkVersion cannot be a resource url, it must be " - + "a literal integer (or string if a preview codename)", null); - } - } - if (tag.equals(TAG_PERMISSION)) { - Attr nameNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME); - if (nameNode != null) { - String name = nameNode.getValue(); - String base = name.substring(name.lastIndexOf('.') + 1); - if (mPermissionNames == null) { - mPermissionNames = Maps.newHashMap(); - } else if (mPermissionNames.containsKey(base)) { - String prevName = mPermissionNames.get(base); - Location location = context.getLocation(nameNode); - NodeList siblings = element.getParentNode().getChildNodes(); - for (int i = 0, n = siblings.getLength(); i < n; i++) { - Node node = siblings.item(i); - if (node == element) { - break; - } else if (node.getNodeType() == Node.ELEMENT_NODE) { - Element sibling = (Element) node; - String suffix = '.' + base; - if (sibling.getTagName().equals(TAG_PERMISSION)) { - String b = element.getAttributeNS(ANDROID_URI, ATTR_NAME); - if (b.endsWith(suffix)) { - Location prevLocation = context.getLocation(node); - prevLocation.setMessage("Previous permission here"); - location.setSecondary(prevLocation); - break; - } - - } - } - } - - String message = String.format("Permission name %1$s is not unique " + - "(appears in both %2$s and %3$s)", base, prevName, name); - context.report(UNIQUE_PERMISSION, element, location, message, null); - } - - mPermissionNames.put(base, name); - } - } - - if (tag.equals(TAG_APPLICATION)) { - mSeenApplication = true; - if (!element.hasAttributeNS(ANDROID_URI, SdkConstants.ATTR_ALLOW_BACKUP) - && context.isEnabled(ALLOW_BACKUP) - && context.getMainProject().getMinSdk() >= 4) { - context.report(ALLOW_BACKUP, element, context.getLocation(element), - "Should explicitly set android:allowBackup to true or " + - "false (it's true by default, and that can have some security " + - "implications for the application's data)", null); - } - } else if (mSeenApplication) { - if (context.isEnabled(ORDER)) { - context.report(ORDER, element, context.getLocation(element), - String.format("<%1$s> tag appears after <application> tag", tag), null); - } - - // Don't complain for *every* element following the <application> tag - mSeenApplication = false; - } - } - - private String getPackage(Element element) { - if (mPackage == null) { - mPackage = element.getOwnerDocument().getDocumentElement().getAttribute(ATTR_PACKAGE); - } - - return mPackage; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MathDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MathDetector.java deleted file mode 100644 index 8709852..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MathDetector.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -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 org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; - -import java.util.Arrays; -import java.util.List; - -/** - * Looks for usages of {@link java.lang.Math} methods which can be replaced with - * {@code android.util.FloatMath} methods to avoid casting. - */ -public class MathDetector extends Detector implements Detector.ClassScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "FloatMath", //$NON-NLS-1$ - "Suggests replacing android.util.FloatMath calls with java.lang.Math", - - "In older versions of Android, using android.util.FloatMath was recommended " + - "for performance reasons when operating on floats. However, on modern hardware " + - "doubles are just as fast as float (though they take more memory), and in " + - "recent versions of Android, FloatMath is actually slower than using java.lang.Math " + - "due to the way the JIT optimizes java.lang.Math. Therefore, you should use " + - "Math instead of FloatMath if you are only targeting Froyo and above.", - - Category.PERFORMANCE, - 3, - Severity.WARNING, - MathDetector.class, - Scope.CLASS_FILE_SCOPE).setMoreInfo( - "http://developer.android.com/guide/practices/design/performance.html#avoidfloat"); //$NON-NLS-1$ - - /** Constructs a new {@link MathDetector} check */ - public MathDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements ClassScanner ---- - - @Override - @Nullable - public List<String> getApplicableCallNames() { - return Arrays.asList( - "sin", //$NON-NLS-1$ - "cos", //$NON-NLS-1$ - "ceil", //$NON-NLS-1$ - "sqrt", //$NON-NLS-1$ - "floor" //$NON-NLS-1$ - ); - } - - @Override - public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode call) { - String owner = call.owner; - - if (owner.equals("android/util/FloatMath") //$NON-NLS-1$ - && context.getProject().getMinSdk() >= 8) { - String message = String.format( - "Use java.lang.Math#%1$s instead of android.util.FloatMath#%1$s() " + - "since it is faster as of API 8", call.name); - context.report(ISSUE, method, call, context.getLocation(call), message, null /*data*/); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java deleted file mode 100644 index 1b76c03..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_BACKGROUND; -import static com.android.SdkConstants.ATTR_FOREGROUND; -import static com.android.SdkConstants.ATTR_LAYOUT; -import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY; -import static com.android.SdkConstants.DOT_JAVA; -import static com.android.SdkConstants.FRAME_LAYOUT; -import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.R_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.VIEW_INCLUDE; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Location.Handle; -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 com.android.utils.Pair; - -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.MethodInvocation; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; - -/** - * Checks whether a root FrameLayout can be replaced with a {@code <merge>} tag. - */ -public class MergeRootFrameLayoutDetector extends LayoutDetector implements Detector.JavaScanner { - /** - * Set of layouts that we want to enable the warning for. We only warn for - * {@code <FrameLayout>}'s that are the root of a layout included from - * another layout, or directly referenced via a {@code setContentView} call. - */ - private Set<String> mWhitelistedLayouts; - - /** - * Set of pending [layout, location] pairs where the given layout is a - * FrameLayout that perhaps should be replaced by a {@code <merge>} tag (if - * the layout is included or set as the content view. This must be processed - * after the whole project has been scanned since the set of includes etc - * can be encountered after the included layout. - */ - private List<Pair<String, Location.Handle>> mPending; - - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "MergeRootFrame", //$NON-NLS-1$ - "Checks whether a root <FrameLayout> can be replaced with a <merge> tag", - "If a `<FrameLayout>` is the root of a layout and does not provide background " + - "or padding etc, it can often be replaced with a `<merge>` tag which is slightly " + - "more efficient. Note that this depends on context, so make sure you understand " + - "how the `<merge>` tag works before proceeding.", - Category.PERFORMANCE, - 4, - Severity.WARNING, - MergeRootFrameLayoutDetector.class, - EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.JAVA_FILE)).setMoreInfo( - "http://android-developers.blogspot.com/2009/03/android-layout-tricks-3-optimize-by.html"); //$NON-NLS-1$ - - /** Constructs a new {@link MergeRootFrameLayoutDetector} */ - public MergeRootFrameLayoutDetector() { - } - - @Override - @NonNull - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return LintUtils.isXmlFile(file) || LintUtils.endsWith(file.getName(), DOT_JAVA); - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (mPending != null && mWhitelistedLayouts != null) { - // Process all the root FrameLayouts that are eligible, and generate - // suggestions for <merge> replacements for any layouts that are included - // from other layouts - for (Pair<String, Handle> pair : mPending) { - String layout = pair.getFirst(); - if (mWhitelistedLayouts.contains(layout)) { - Handle handle = pair.getSecond(); - - Object clientData = handle.getClientData(); - if (clientData instanceof Node) { - if (context.getDriver().isSuppressed(ISSUE, (Node) clientData)) { - return; - } - } - - Location location = handle.resolve(); - context.report(ISSUE, location, - "This <FrameLayout> can be replaced with a <merge> tag", null); - } - } - } - } - - // Implements XmlScanner - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList(VIEW_INCLUDE, FRAME_LAYOUT); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String tag = element.getTagName(); - if (tag.equals(VIEW_INCLUDE)) { - String layout = element.getAttribute(ATTR_LAYOUT); // NOTE: Not in android: namespace - if (layout.startsWith(LAYOUT_RESOURCE_PREFIX)) { // Ignore @android:layout/ layouts - layout = layout.substring(LAYOUT_RESOURCE_PREFIX.length()); - whiteListLayout(layout); - } - } else { - assert tag.equals(FRAME_LAYOUT); - if (LintUtils.isRootElement(element) && - ((isWidthFillParent(element) && isHeightFillParent(element)) || - !element.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_GRAVITY)) - && !element.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND) - && !element.hasAttributeNS(ANDROID_URI, ATTR_FOREGROUND) - && !hasPadding(element)) { - String layout = LintUtils.getLayoutName(context.file); - Handle handle = context.parser.createLocationHandle(context, element); - handle.setClientData(element); - - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - if (mPending == null) { - mPending = new ArrayList<Pair<String,Handle>>(); - } - mPending.add(Pair.of(layout, handle)); - } - } - } - - private void whiteListLayout(String layout) { - if (mWhitelistedLayouts == null) { - mWhitelistedLayouts = new HashSet<String>(); - } - mWhitelistedLayouts.add(layout); - } - - // Implements JavaScanner - - @Override - public List<String> getApplicableMethodNames() { - return Collections.singletonList("setContentView"); //$NON-NLS-1$ - } - - @Override - public void visitMethod( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - StrictListAccessor<Expression, MethodInvocation> argumentList = node.astArguments(); - if (argumentList != null && argumentList.size() == 1) { - Expression argument = argumentList.first(); - if (argument instanceof Select) { - String expression = argument.toString(); - if (expression.startsWith(R_LAYOUT_RESOURCE_PREFIX)) { - whiteListLayout(expression.substring(R_LAYOUT_RESOURCE_PREFIX.length())); - } - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingClassDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingClassDetector.java deleted file mode 100644 index 8002c40..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingClassDetector.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * 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.ANDROID_PKG_PREFIX; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_CLASS; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_PACKAGE; -import static com.android.SdkConstants.CONSTRUCTOR_NAME; -import static com.android.SdkConstants.TAG_ACTIVITY; -import static com.android.SdkConstants.TAG_APPLICATION; -import static com.android.SdkConstants.TAG_PROVIDER; -import static com.android.SdkConstants.TAG_RECEIVER; -import static com.android.SdkConstants.TAG_SERVICE; -import static com.android.SdkConstants.TAG_STRING; -import static com.android.SdkConstants.VIEW_FRAGMENT; -import static com.android.SdkConstants.VIEW_TAG; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -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.ClassScanner; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Location.Handle; -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 com.android.utils.SdkUtils; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Checks to ensure that classes referenced in the manifest actually exist and are included - * - */ -public class MissingClassDetector extends LayoutDetector implements ClassScanner { - /** Manifest-referenced classes missing from the project or libraries */ - public static final Issue MISSING = Issue.create( - "MissingRegistered", //$NON-NLS-1$ - "Ensures that classes referenced in the manifest are present in the project or libraries", - - "If a class is referenced in the manifest, it must also exist in the project (or in one " + - "of the libraries included by the project. This check helps uncover typos in " + - "registration names, or attempts to rename or move classes without updating the " + - "manifest file properly.", - - Category.CORRECTNESS, - 8, - Severity.ERROR, - MissingClassDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.CLASS_FILE, Scope.JAVA_LIBRARIES, Scope.RESOURCE_FILE)) - .setMoreInfo("http://developer.android.com/guide/topics/manifest/manifest-intro.html"); //$NON-NLS-1$ - - /** Are activity, service, receiver etc subclasses instantiatable? */ - public static final Issue INSTANTIATABLE = Issue.create( - "Instantiatable", //$NON-NLS-1$ - "Ensures that classes registered in the manifest file are instantiatable", - - "Activities, services, broadcast receivers etc. registered in the manifest file " + - "must be \"instantiatable\" by the system, which means that the class must be " + - "public, it must have an empty public constructor, and if it's an inner class, " + - "it must be a static inner class.", - - Category.CORRECTNESS, - 6, - Severity.WARNING, - MissingClassDetector.class, - Scope.CLASS_FILE_SCOPE); - - /** Is the right character used for inner class separators? */ - public static final Issue INNERCLASS = Issue.create( - "InnerclassSeparator", //$NON-NLS-1$ - "Ensures that inner classes are referenced using '$' instead of '.' in class names", - - "When you reference an inner class in a manifest file, you must use '$' instead of '.' " + - "as the separator character, i.e. Outer$Inner instead of Outer.Inner.\n" + - "\n" + - "(If you get this warning for a class which is not actually an inner class, it's " + - "because you are using uppercase characters in your package name, which is not " + - "conventional.)", - - Category.CORRECTNESS, - 3, - Severity.WARNING, - MissingClassDetector.class, - Scope.MANIFEST_SCOPE); - - private Map<String, Location.Handle> mReferencedClasses; - private Set<String> mCustomViews; - private boolean mHaveClasses; - - /** Constructs a new {@link MissingClassDetector} */ - public MissingClassDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements XmlScanner ---- - - @Override - public Collection<String> getApplicableElements() { - return ALL; - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.VALUES || folderType == ResourceFolderType.LAYOUT; - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String pkg = null; - Node classNameNode; - String className; - String tag = element.getTagName(); - ResourceFolderType folderType = context.getResourceFolderType(); - if (folderType == ResourceFolderType.VALUES) { - if (!tag.equals(TAG_STRING)) { - return; - } - Attr attr = element.getAttributeNode(ATTR_NAME); - if (attr == null) { - return; - } - className = attr.getValue(); - classNameNode = attr; - } else if (folderType == ResourceFolderType.LAYOUT) { - if (tag.indexOf('.') > 0) { - className = tag; - classNameNode = element; - } else if (tag.equals(VIEW_FRAGMENT) || tag.equals(VIEW_TAG)) { - Attr attr = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME); - if (attr == null) { - attr = element.getAttributeNode(ATTR_CLASS); - } - if (attr == null) { - return; - } - className = attr.getValue(); - classNameNode = attr; - } else { - return; - } - } else { - // Manifest file - if (TAG_APPLICATION.equals(tag) - || TAG_ACTIVITY.equals(tag) - || TAG_SERVICE.equals(tag) - || TAG_RECEIVER.equals(tag) - || TAG_PROVIDER.equals(tag)) { - Element root = element.getOwnerDocument().getDocumentElement(); - pkg = root.getAttribute(ATTR_PACKAGE); - Attr attr = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME); - if (attr == null) { - return; - } - className = attr.getValue(); - classNameNode = attr; - } else { - return; - } - } - if (className.isEmpty()) { - return; - } - - String fqcn; - int dotIndex = className.indexOf('.'); - if (dotIndex <= 0) { - if (pkg == null) { - return; // value file - } - if (dotIndex == 0) { - fqcn = pkg + className; - } else { - // According to the <activity> manifest element documentation, this is not - // valid ( http://developer.android.com/guide/topics/manifest/activity-element.html ) - // but it appears in manifest files and appears to be supported by the runtime - // so handle this in code as well: - fqcn = pkg + '.' + className; - } - } else { // else: the class name is already a fully qualified class name - fqcn = className; - // Only look for fully qualified tracker names in analytics files - if (folderType == ResourceFolderType.VALUES - && !SdkUtils.endsWith(context.file.getPath(), "analytics.xml")) { //$NON-NLS-1$ - return; - } - } - - String signature = ClassContext.getInternalName(fqcn); - if (signature.isEmpty() || signature.startsWith(ANDROID_PKG_PREFIX)) { - return; - } - - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - Handle handle = null; - if (!context.getDriver().isSuppressed(MISSING, element)) { - if (mReferencedClasses == null) { - mReferencedClasses = Maps.newHashMapWithExpectedSize(16); - mCustomViews = Sets.newHashSetWithExpectedSize(8); - } - - handle = context.parser.createLocationHandle(context, element); - mReferencedClasses.put(signature, handle); - if (folderType == ResourceFolderType.LAYOUT && !tag.equals(VIEW_FRAGMENT)) { - mCustomViews.add(ClassContext.getInternalName(className)); - } - } - - if (signature.indexOf('$') != -1 && pkg != null) { - if (className.indexOf('$') == -1 && className.indexOf('.', 1) > 0) { - boolean haveUpperCase = false; - for (int i = 0, n = pkg.length(); i < n; i++) { - if (Character.isUpperCase(pkg.charAt(i))) { - haveUpperCase = true; - break; - } - } - if (!haveUpperCase) { - String message = "Use '$' instead of '.' for inner classes " + - "(or use only lowercase letters in package names)"; - Location location = context.getLocation(classNameNode); - context.report(INNERCLASS, element, location, message, null); - } - } - - // The internal name contains a $ which means it's an inner class. - // The conversion from fqcn to internal name is a bit ambiguous: - // "a.b.C.D" usually means "inner class D in class C in package a.b". - // However, it can (see issue 31592) also mean class D in package "a.b.C". - // To make sure we don't falsely complain that foo/Bar$Baz doesn't exist, - // in case the user has actually created a package named foo/Bar and a proper - // class named Baz, we register *both* into the reference map. - // When generating errors we'll look for these an rip them back out if - // it looks like one of the two variations have been seen. - if (handle != null) { - signature = signature.replace('$', '/'); - mReferencedClasses.put(signature, handle); - } - } - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (!context.getProject().isLibrary() && mHaveClasses - && mReferencedClasses != null && !mReferencedClasses.isEmpty() - && context.getDriver().getScope().contains(Scope.CLASS_FILE)) { - List<String> classes = new ArrayList<String>(mReferencedClasses.keySet()); - Collections.sort(classes); - for (String owner : classes) { - Location.Handle handle = mReferencedClasses.get(owner); - String fqcn = ClassContext.getFqcn(owner); - - String signature = ClassContext.getInternalName(fqcn); - if (!signature.equals(owner)) { - if (!mReferencedClasses.containsKey(signature)) { - continue; - } - } else { - signature = signature.replace('$', '/'); - if (!mReferencedClasses.containsKey(signature)) { - continue; - } - } - mReferencedClasses.remove(owner); - - // Ignore usages of platform libraries - if (owner.startsWith("android/")) { //$NON-NLS-1$ - continue; - } - - String message = String.format( - "Class referenced in the manifest, %1$s, was not found in the " + - "project or the libraries", fqcn); - Location location = handle.resolve(); - File parentFile = location.getFile().getParentFile(); - if (parentFile != null) { - String parent = parentFile.getName(); - ResourceFolderType type = ResourceFolderType.getFolderType(parent); - if (type == ResourceFolderType.LAYOUT) { - message = String.format( - "Class referenced in the layout file, %1$s, was not found in " - + "the project or the libraries", fqcn); - } else if (type == ResourceFolderType.VALUES) { - message = String.format( - "Class referenced in the analytics file, %1$s, was not " - + "found in the project or the libraries", fqcn); - } - } - - context.report(MISSING, location, message, null); - } - } - } - - // ---- Implements ClassScanner ---- - - @Override - public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { - if (!mHaveClasses && !context.isFromClassLibrary() - && context.getProject() == context.getMainProject()) { - mHaveClasses = true; - } - String curr = classNode.name; - if (mReferencedClasses != null && mReferencedClasses.containsKey(curr)) { - boolean isCustomView = mCustomViews.contains(curr); - mReferencedClasses.remove(curr); - - // Ensure that the class is public, non static and has a null constructor! - - if ((classNode.access & Opcodes.ACC_PUBLIC) == 0) { - context.report(INSTANTIATABLE, context.getLocation(classNode), String.format( - "This class should be public (%1$s)", - ClassContext.createSignature(classNode.name, null, null)), - null); - return; - } - - if (classNode.name.indexOf('$') != -1 && !LintUtils.isStaticInnerClass(classNode)) { - context.report(INSTANTIATABLE, context.getLocation(classNode), String.format( - "This inner class should be static (%1$s)", - ClassContext.createSignature(classNode.name, null, null)), - null); - return; - } - - boolean hasDefaultConstructor = false; - @SuppressWarnings("rawtypes") // ASM API - List methodList = classNode.methods; - for (Object m : methodList) { - MethodNode method = (MethodNode) m; - if (method.name.equals(CONSTRUCTOR_NAME)) { - if (method.desc.equals("()V")) { //$NON-NLS-1$ - // The constructor must be public - if ((method.access & Opcodes.ACC_PUBLIC) != 0) { - hasDefaultConstructor = true; - } else { - context.report(INSTANTIATABLE, context.getLocation(method, classNode), - "The default constructor must be public", - null); - // Also mark that we have a constructor so we don't complain again - // below since we've already emitted a more specific error related - // to the default constructor - hasDefaultConstructor = true; - } - } - } - } - - if (!hasDefaultConstructor && !isCustomView && !context.isFromClassLibrary() - && context.getProject().getReportIssues()) { - context.report(INSTANTIATABLE, context.getLocation(classNode), String.format( - "This class should provide a default constructor (a public " + - "constructor with no arguments) (%1$s)", - ClassContext.createSignature(classNode.name, null, null)), - null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingIdDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingIdDetector.java deleted file mode 100644 index 1b79600..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingIdDetector.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.ANDROID_URI; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_TAG; -import static com.android.SdkConstants.VIEW_FRAGMENT; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -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; -import java.util.Collections; - -/** - * Check which looks for missing id's in views where they are probably needed - */ -public class MissingIdDetector extends LayoutDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "MissingId", //$NON-NLS-1$ - "Ensures that XML tags like <fragment> specify an id or tag attribute", - - "If you do not specify an android:id or an android:tag attribute on a " + - "<fragment> element, then if the activity is restarted (for example for " + - "an orientation rotation) you may lose state. From the fragment " + - "documentation:\n" + - "\n" + - "\"Each fragment requires a unique identifier that the system can use " + - "to restore the fragment if the activity is restarted (and which you can " + - "use to capture the fragment to perform transactions, such as remove it). " + - "* Supply the android:id attribute with a unique ID.\n" + - "* Supply the android:tag attribute with a unique string.\n" + - "If you provide neither of the previous two, the system uses the ID of the " + - "container view.", - - Category.CORRECTNESS, - 5, - Severity.WARNING, - MissingIdDetector.class, - Scope.RESOURCE_FILE_SCOPE) - .setMoreInfo("http://developer.android.com/guide/components/fragments.html"); //$NON-NLS-1$ - - /** Constructs a new {@link MissingIdDetector} */ - public MissingIdDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList(VIEW_FRAGMENT); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (!element.hasAttributeNS(ANDROID_URI, ATTR_ID) && - !element.hasAttributeNS(ANDROID_URI, ATTR_TAG)) { - context.report(ISSUE, element, context.getLocation(element), - "This <fragment> tag should specify an id or a tag to preserve state " + - "across activity restarts", null); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java deleted file mode 100644 index 0b6ab02..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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.ANDROID_URI; -import static com.android.SdkConstants.AUTO_URI; -import static com.android.SdkConstants.URI_PREFIX; -import static com.android.SdkConstants.XMLNS_PREFIX; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -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.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.HashMap; -import java.util.Map; - -/** - * Checks for various issues related to XML namespaces - */ -public class NamespaceDetector extends LayoutDetector { - /** Typos in the namespace */ - public static final Issue TYPO = Issue.create( - "NamespaceTypo", //$NON-NLS-1$ - "Looks for misspellings in namespace declarations", - - "Accidental misspellings in namespace declarations can lead to some very " + - "obscure error messages. This check looks for potential misspellings to " + - "help track these down.", - Category.CORRECTNESS, - 8, - Severity.WARNING, - NamespaceDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Unused namespace declarations */ - public static final Issue UNUSED = Issue.create( - "UnusedNamespace", //$NON-NLS-1$ - "Finds unused namespaces in XML documents", - - "Unused namespace declarations take up space and require processing that is not " + - "necessary", - - Category.PERFORMANCE, - 1, - Severity.WARNING, - NamespaceDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Using custom namespace attributes in a library project */ - public static final Issue CUSTOMVIEW = Issue.create( - "LibraryCustomView", //$NON-NLS-1$ - "Flags custom attributes in libraries, which must use the res-auto-namespace instead", - - "When using a custom view with custom attributes in a library project, the layout " + - "must use the special namespace " + AUTO_URI + " instead of a URI which includes " + - "the library project's own package. This will be used to automatically adjust the " + - "namespace of the attributes when the library resources are merged into the " + - "application project.", - Category.CORRECTNESS, - 6, - Severity.ERROR, - NamespaceDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Prefix relevant for custom namespaces */ - private static final String XMLNS_ANDROID = "xmlns:android"; //$NON-NLS-1$ - private static final String XMLNS_A = "xmlns:a"; //$NON-NLS-1$ - - private Map<String, Attr> mUnusedNamespaces; - private boolean mCheckUnused; - - /** Constructs a new {@link NamespaceDetector} */ - public NamespaceDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public void visitDocument(@NonNull XmlContext context, @NonNull Document document) { - boolean haveCustomNamespace = false; - Element root = document.getDocumentElement(); - NamedNodeMap attributes = root.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Node item = attributes.item(i); - if (item.getNodeName().startsWith(XMLNS_PREFIX)) { - String value = item.getNodeValue(); - - if (!value.equals(ANDROID_URI)) { - Attr attribute = (Attr) item; - - if (value.startsWith(URI_PREFIX)) { - haveCustomNamespace = true; - if (mUnusedNamespaces == null) { - mUnusedNamespaces = new HashMap<String, Attr>(); - } - mUnusedNamespaces.put(item.getNodeName().substring(XMLNS_PREFIX.length()), - attribute); - } else if (!value.startsWith("http://")) { //$NON-NLS-1$ - context.report(TYPO, attribute, context.getLocation(attribute), - "Suspicious namespace: should start with http://", null); - - continue; - } - - String name = attribute.getName(); - if (!name.equals(XMLNS_ANDROID) && !name.equals(XMLNS_A)) { - // See if it looks like a typo - int resIndex = value.indexOf("/res/"); //$NON-NLS-1$ - if (resIndex != -1 && value.length() + 5 > URI_PREFIX.length()) { - String urlPrefix = value.substring(0, resIndex + 5); - if (!urlPrefix.equals(URI_PREFIX) && - LintUtils.editDistance(URI_PREFIX, urlPrefix) <= 3) { - String correctUri = URI_PREFIX + value.substring(resIndex + 5); - context.report(TYPO, attribute, context.getLocation(attribute), - String.format( - "Possible typo in URL: was \"%1$s\", should " + - "probably be \"%2$s\"", - value, correctUri), - null); - } - } - continue; - } - - if (!context.isEnabled(TYPO)) { - continue; - } - - if (name.equals(XMLNS_A)) { - // For the "android" prefix we always assume that the namespace prefix - // should be our expected prefix, but for the "a" prefix we make sure - // that it's at least "close"; if you're bound it to something completely - // different, don't complain. - if (LintUtils.editDistance(ANDROID_URI, value) > 4) { - continue; - } - } - - if (value.equalsIgnoreCase(ANDROID_URI)) { - context.report(TYPO, attribute, context.getLocation(attribute), - String.format( - "URI is case sensitive: was \"%1$s\", expected \"%2$s\"", - value, ANDROID_URI), null); - } else { - context.report(TYPO, attribute, context.getLocation(attribute), - String.format( - "Unexpected namespace URI bound to the \"android\" " + - "prefix, was %1$s, expected %2$s", value, ANDROID_URI), - null); - } - } - } - } - - if (haveCustomNamespace) { - boolean checkCustomAttrs = context.isEnabled(CUSTOMVIEW) && context.getProject().isLibrary(); - mCheckUnused = context.isEnabled(UNUSED); - - if (checkCustomAttrs) { - checkCustomNamespace(context, root); - } - checkElement(context, root); - - if (mCheckUnused && !mUnusedNamespaces.isEmpty()) { - for (Map.Entry<String, Attr> entry : mUnusedNamespaces.entrySet()) { - String prefix = entry.getKey(); - Attr attribute = entry.getValue(); - context.report(UNUSED, attribute, context.getLocation(attribute), - String.format("Unused namespace %1$s", prefix), null); - } - } - } - } - - private static void checkCustomNamespace(XmlContext context, Element element) { - NamedNodeMap attributes = element.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Attr attribute = (Attr) attributes.item(i); - if (attribute.getName().startsWith(XMLNS_PREFIX)) { - String uri = attribute.getValue(); - if (uri != null && !uri.isEmpty() && uri.startsWith(URI_PREFIX) - && !uri.equals(ANDROID_URI)) { - context.report(CUSTOMVIEW, attribute, context.getLocation(attribute), - "When using a custom namespace attribute in a library project, " + - "use the namespace \"" + AUTO_URI + "\" instead.", null); - } - } - } - } - - private void checkElement(XmlContext context, Node node) { - if (node.getNodeType() == Node.ELEMENT_NODE) { - if (mCheckUnused) { - NamedNodeMap attributes = node.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Attr attribute = (Attr) attributes.item(i); - String prefix = attribute.getPrefix(); - if (prefix != null) { - mUnusedNamespaces.remove(prefix); - } - } - } - - NodeList childNodes = node.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - checkElement(context, childNodes.item(i)); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java deleted file mode 100644 index 4650a8f..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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 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.LIST_VIEW; -import static com.android.SdkConstants.SCROLL_VIEW; - -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.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 org.w3c.dom.Node; - -import java.util.Arrays; -import java.util.Collection; - -/** - * Checks whether a root FrameLayout can be replaced with a {@code <merge>} tag. - */ -public class NestedScrollingWidgetDetector extends LayoutDetector { - private int mVisitingHorizontalScroll; - private int mVisitingVerticalScroll; - - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "NestedScrolling", //$NON-NLS-1$ - "Checks whether a scrolling widget has any nested scrolling widgets within", - // TODO: Better description! - "A scrolling widget such as a `ScrollView` should not contain any nested " + - "scrolling widgets since this has various usability issues", - Category.CORRECTNESS, - 7, - Severity.WARNING, - NestedScrollingWidgetDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new {@link NestedScrollingWidgetDetector} */ - public NestedScrollingWidgetDetector() { - } - - @Override - public void beforeCheckFile(@NonNull Context context) { - mVisitingHorizontalScroll = 0; - mVisitingVerticalScroll = 0; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - SCROLL_VIEW, - LIST_VIEW, - GRID_VIEW, - // Horizontal - GALLERY, - HORIZONTAL_SCROLL_VIEW - ); - } - - private Element findOuterScrollingWidget(Node node, boolean vertical) { - Collection<String> applicableElements = getApplicableElements(); - while (node != null) { - if (node instanceof Element) { - Element element = (Element) node; - String tagName = element.getTagName(); - if (applicableElements.contains(tagName) - && vertical == isVerticalScroll(element)) { - return element; - } - } - - node = node.getParentNode(); - } - - return null; - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - boolean vertical = isVerticalScroll(element); - if (vertical) { - mVisitingVerticalScroll++; - } else { - mVisitingHorizontalScroll++; - } - - if (mVisitingHorizontalScroll > 1 || mVisitingVerticalScroll > 1) { - Element parent = findOuterScrollingWidget(element.getParentNode(), vertical); - if (parent != null) { - String format; - if (mVisitingVerticalScroll > 1) { - format = "The vertically scrolling %1$s should not contain another " + - "vertically scrolling widget (%2$s)"; - } else { - format = "The horizontally scrolling %1$s should not contain another " + - "horizontally scrolling widget (%2$s)"; - } - String msg = String.format(format, parent.getTagName(), element.getTagName()); - context.report(ISSUE, element, context.getLocation(element), msg, null); - } - } - } - - @Override - public void visitElementAfter(@NonNull XmlContext context, @NonNull Element element) { - if (isVerticalScroll(element)) { - mVisitingVerticalScroll--; - assert mVisitingVerticalScroll >= 0; - } else { - mVisitingHorizontalScroll--; - assert mVisitingHorizontalScroll >= 0; - } - } - - private static boolean isVerticalScroll(Element element) { - String view = element.getTagName(); - if (view.equals(GALLERY) || view.equals(HORIZONTAL_SCROLL_VIEW)) { - return false; - } else { - // This method should only be called with one of the 5 widget types - // listed in getApplicableElements - assert view.equals(SCROLL_VIEW) || view.equals(LIST_VIEW) || view.equals(GRID_VIEW); - return true; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/NonInternationalizedSmsDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/NonInternationalizedSmsDetector.java deleted file mode 100644 index e970572..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/NonInternationalizedSmsDetector.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.Scope; -import com.android.tools.lint.detector.api.Severity; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.MethodInvocation; -import lombok.ast.StrictListAccessor; -import lombok.ast.StringLiteral; - -/** Detector looking for text messages sent to an unlocalized phone number. */ -public class NonInternationalizedSmsDetector extends Detector implements Detector.JavaScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "UnlocalizedSms", //$NON-NLS-1$ - "Looks for code sending text messages to unlocalized phone numbers", - - "SMS destination numbers must start with a country code or the application code " + - "must ensure that the SMS is only sent when the user is in the same country as " + - "the receiver.", - - Category.CORRECTNESS, - 5, - Severity.WARNING, - NonInternationalizedSmsDetector.class, - Scope.JAVA_FILE_SCOPE); - - - /** Constructs a new {@link NonInternationalizedSmsDetector} check */ - public NonInternationalizedSmsDetector() { - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - - // ---- Implements JavaScanner ---- - - @Override - public List<String> getApplicableMethodNames() { - List<String> methodNames = new ArrayList<String>(2); - methodNames.add("sendTextMessage"); //$NON-NLS-1$ - methodNames.add("sendMultipartTextMessage"); //$NON-NLS-1$ - return methodNames; - } - - @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - assert node.astName().astValue().equals("sendTextMessage") || //$NON-NLS-1$ - node.astName().astValue().equals("sendMultipartTextMessage"); //$NON-NLS-1$ - if (node.astOperand() == null) { - // "sendTextMessage"/"sendMultipartTextMessage" in the code with no operand - return; - } - - StrictListAccessor<Expression, MethodInvocation> args = node.astArguments(); - if (args.size() == 5) { - Expression destinationAddress = args.first(); - if (destinationAddress instanceof StringLiteral) { - String number = ((StringLiteral) destinationAddress).astValue(); - - if (!number.startsWith("+")) { //$NON-NLS-1$ - context.report(ISSUE, node, context.getLocation(destinationAddress), - "To make sure the SMS can be sent by all users, please start the SMS number " + - "with a + and a country code or restrict the code invocation to people in the country " + - "you are targeting.", - null); - } - } - } - } -}
\ No newline at end of file diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetector.java deleted file mode 100644 index 559a7ad..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetector.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ABSOLUTE_LAYOUT; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_LAYOUT; -import static com.android.SdkConstants.ATTR_LAYOUT_ABOVE; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BASELINE; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BOTTOM; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_BOTTOM; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING; -import static com.android.SdkConstants.ATTR_LAYOUT_BELOW; -import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_HORIZONTAL; -import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_IN_PARENT; -import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_VERTICAL; -import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN; -import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN_SPAN; -import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_BOTTOM; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_LAYOUT_ROW; -import static com.android.SdkConstants.ATTR_LAYOUT_ROW_SPAN; -import static com.android.SdkConstants.ATTR_LAYOUT_SPAN; -import static com.android.SdkConstants.ATTR_LAYOUT_TO_LEFT_OF; -import static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF; -import static com.android.SdkConstants.ATTR_LAYOUT_WEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ATTR_LAYOUT_X; -import static com.android.SdkConstants.ATTR_LAYOUT_Y; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.GRID_LAYOUT; -import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.RELATIVE_LAYOUT; -import static com.android.SdkConstants.TABLE_ROW; -import static com.android.SdkConstants.VIEW_INCLUDE; -import static com.android.SdkConstants.VIEW_MERGE; -import static com.android.SdkConstants.VIEW_TAG; - -import com.android.annotations.NonNull; -import com.android.tools.lint.client.api.IDomParser; -import com.android.tools.lint.client.api.SdkInfo; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Location.Handle; -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 com.android.utils.Pair; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Looks for layout params on views that are "obsolete" - may have made sense - * when the view was added but there is a different layout parent now which does - * not use the given layout params. - */ -public class ObsoleteLayoutParamsDetector extends LayoutDetector { - /** Usage of deprecated views or attributes */ - public static final Issue ISSUE = Issue.create( - "ObsoleteLayoutParam", //$NON-NLS-1$ - "Looks for layout params that are not valid for the given parent layout", - "The given layout_param is not defined for the given layout, meaning it has no " + - "effect. This usually happens when you change the parent layout or move view " + - "code around without updating the layout params. This will cause useless " + - "attribute processing at runtime, and is misleading for others reading the " + - "layout so the parameter should be removed.", - Category.PERFORMANCE, - 6, - Severity.WARNING, - ObsoleteLayoutParamsDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** - * Set of layout parameter names that are considered valid no matter what so - * no other checking is necessary - such as layout_width and layout_height. - */ - private static final Set<String> VALID = new HashSet<String>(10); - - /** - * Mapping from a layout parameter name (local name only) to the defining - * ViewGroup. Note that it's possible for the same name to be defined by - * multiple ViewGroups - but it turns out this is extremely rare (the only - * examples are layout_column defined by both TableRow and GridLayout, and - * layout_gravity defined by many layouts) so rather than handle this with - * every single layout attribute pointing to a list, this is just special - * cased instead. - */ - private static final Map<String, String> PARAM_TO_VIEW = new HashMap<String, String>(28); - - static { - // Available (mostly) everywhere: No check - VALID.add(ATTR_LAYOUT_WIDTH); - VALID.add(ATTR_LAYOUT_HEIGHT); - - // The layout_gravity isn't "global" but it's defined on many of the most - // common layouts (FrameLayout, LinearLayout and GridLayout) so we don't - // currently check for it. In order to do this we'd need to make the map point - // to lists rather than individual layouts or we'd need a bunch of special cases - // like the one done for layout_column below. - VALID.add(ATTR_LAYOUT_GRAVITY); - - // From ViewGroup.MarginLayoutParams - VALID.add(ATTR_LAYOUT_MARGIN_LEFT); - VALID.add(ATTR_LAYOUT_MARGIN_RIGHT); - VALID.add(ATTR_LAYOUT_MARGIN_TOP); - VALID.add(ATTR_LAYOUT_MARGIN_BOTTOM); - VALID.add(ATTR_LAYOUT_MARGIN); - - // Absolute Layout - PARAM_TO_VIEW.put(ATTR_LAYOUT_X, ABSOLUTE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_Y, ABSOLUTE_LAYOUT); - - // Linear Layout - PARAM_TO_VIEW.put(ATTR_LAYOUT_WEIGHT, LINEAR_LAYOUT); - - // Grid Layout - PARAM_TO_VIEW.put(ATTR_LAYOUT_COLUMN, GRID_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_COLUMN_SPAN, GRID_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ROW, GRID_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ROW_SPAN, GRID_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ROW_SPAN, GRID_LAYOUT); - - // Table Layout - // ATTR_LAYOUT_COLUMN is defined for both GridLayout and TableLayout, - // so we don't want to do - // PARAM_TO_VIEW.put(ATTR_LAYOUT_COLUMN, TABLE_ROW); - // here since it would wipe out the above GridLayout registration. - // Since this is the only case where there is a conflict (in addition to layout_gravity - // which is defined in many places), rather than making the map point to lists - // this specific case is just special cased below, look for ATTR_LAYOUT_COLUMN. - PARAM_TO_VIEW.put(ATTR_LAYOUT_SPAN, TABLE_ROW); - - // Relative Layout - PARAM_TO_VIEW.put(ATTR_LAYOUT_ALIGN_LEFT, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ALIGN_RIGHT, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ALIGN_TOP, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ALIGN_BOTTOM, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ALIGN_PARENT_TOP, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ALIGN_PARENT_LEFT, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ALIGN_PARENT_RIGHT, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ALIGN_BASELINE, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_CENTER_IN_PARENT, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_CENTER_VERTICAL, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_CENTER_HORIZONTAL, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_TO_RIGHT_OF, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_TO_LEFT_OF, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_BELOW, RELATIVE_LAYOUT); - PARAM_TO_VIEW.put(ATTR_LAYOUT_ABOVE, RELATIVE_LAYOUT); - } - - /** - * Map from an included layout to all the including contexts (each including - * context is a pair of a file containing the include to the parent tag at - * the included location) - */ - private Map<String, List<Pair<File, String>>> mIncludes; - - /** - * List of pending include checks. When a layout parameter attribute is - * found on a root element, or on a child of a {@code merge} root tag, then - * we want to check across layouts whether the including context (the parent - * of the include tag) is valid for this attribute. We cannot check this - * immediately because we are processing the layouts in an arbitrary order - * so the included layout may be seen before the including layout and so on. - * Therefore, we stash these attributes to be checked after we're done. Each - * pair is a pair of an attribute name to be checked, and the file that - * attribute is referenced in. - */ - private final List<Pair<String, Location.Handle>> mPending = - new ArrayList<Pair<String,Location.Handle>>(); - - /** Constructs a new {@link ObsoleteLayoutParamsDetector} */ - public ObsoleteLayoutParamsDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList(VIEW_INCLUDE); - } - - @Override - public Collection<String> getApplicableAttributes() { - return ALL; - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - String name = attribute.getLocalName(); - if (name != null && name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) - && ANDROID_URI.equals(attribute.getNamespaceURI())) { - if (VALID.contains(name)) { - return; - } - - String parent = PARAM_TO_VIEW.get(name); - if (parent != null) { - Element viewElement = attribute.getOwnerElement(); - Node layoutNode = viewElement.getParentNode(); - if (layoutNode == null || layoutNode.getNodeType() != Node.ELEMENT_NODE) { - // This is a layout attribute on a root element; this presumably means - // that this layout is included so check the included layouts to make - // sure at least one included context is valid for this layout_param. - // We can't do that yet since we may be processing the include tag to - // this layout after the layout itself. Instead, stash a work order... - if (context.getScope().contains(Scope.ALL_RESOURCE_FILES)) { - IDomParser parser = context.parser; - Location.Handle handle = parser.createLocationHandle(context, attribute); - handle.setClientData(attribute); - mPending.add(Pair.of(name, handle)); - } - - return; - } - - String parentTag = ((Element) layoutNode).getTagName(); - if (parentTag.equals(VIEW_MERGE)) { - // This is a merge which means we need to check the including contexts, - // wherever they are. This has to be done after all the files have been - // scanned since we are not processing the files in any particular order. - if (context.getScope().contains(Scope.ALL_RESOURCE_FILES)) { - IDomParser parser = context.parser; - Location.Handle handle = parser.createLocationHandle(context, attribute); - handle.setClientData(attribute); - mPending.add(Pair.of(name, handle)); - } - - return; - } - - if (!isValidParamForParent(context, name, parent, parentTag)) { - if (name.equals(ATTR_LAYOUT_COLUMN) - && isValidParamForParent(context, name, TABLE_ROW, parentTag)) { - return; - } - context.report(ISSUE, attribute, context.getLocation(attribute), - String.format("Invalid layout param in a %1$s: %2$s", parentTag, name), - null); - } - } else { - // We could warn about unknown layout params but this might be brittle if - // new params are added or if people write custom ones; this is just a log - // for us to track these and update the check as necessary: - //context.client.log(null, - // String.format("Unrecognized layout param '%1$s'", name)); - } - } - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String layout = element.getAttribute(ATTR_LAYOUT); - if (layout.startsWith(LAYOUT_RESOURCE_PREFIX)) { // Ignore @android:layout/ layouts - layout = layout.substring(LAYOUT_RESOURCE_PREFIX.length()); - - Node parent = element.getParentNode(); - if (parent.getNodeType() == Node.ELEMENT_NODE) { - String tag = parent.getNodeName(); - if (tag.indexOf('.') == -1 && !tag.equals(VIEW_MERGE)) { - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - if (mIncludes == null) { - mIncludes = new HashMap<String, List<Pair<File, String>>>(); - } - List<Pair<File, String>> includes = mIncludes.get(layout); - if (includes == null) { - includes = new ArrayList<Pair<File, String>>(); - mIncludes.put(layout, includes); - } - includes.add(Pair.of(context.file, tag)); - } - } - } - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (mIncludes == null) { - return; - } - - for (Pair<String, Location.Handle> pending : mPending) { - Handle handle = pending.getSecond(); - Location location = handle.resolve(); - File file = location.getFile(); - String layout = file.getName(); - if (layout.endsWith(DOT_XML)) { - layout = layout.substring(0, layout.length() - DOT_XML.length()); - } - - List<Pair<File, String>> includes = mIncludes.get(layout); - if (includes == null) { - // Nobody included this file - continue; - } - - String name = pending.getFirst(); - String parent = PARAM_TO_VIEW.get(name); - if (parent == null) { - continue; - } - - boolean isValid = false; - for (Pair<File, String> include : includes) { - String parentTag = include.getSecond(); - if (isValidParamForParent(context, name, parent, parentTag)) { - isValid = true; - break; - } else if (!isValid && name.equals(ATTR_LAYOUT_COLUMN) - && isValidParamForParent(context, name, TABLE_ROW, parentTag)) { - isValid = true; - break; - } - } - - if (!isValid) { - Object clientData = handle.getClientData(); - if (clientData instanceof Node) { - if (context.getDriver().isSuppressed(ISSUE, (Node) clientData)) { - return; - } - } - - StringBuilder sb = new StringBuilder(40); - for (Pair<File, String> include : includes) { - if (sb.length() > 0) { - sb.append(", "); //$NON-NLS-1$ - } - File from = include.getFirst(); - String parentTag = include.getSecond(); - sb.append(String.format("included from within a %1$s in %2$s", - parentTag, - from.getParentFile().getName() + File.separator + from.getName())); - } - String message = String.format("Invalid layout param '%1$s' (%2$s)", - name, sb.toString()); - // TODO: Compute applicable scope node - context.report(ISSUE, location, message, null); - } - } - } - - /** - * Checks whether the given layout parameter name is valid for the given - * parent tag assuming it has the given current parent tag - */ - private static boolean isValidParamForParent(Context context, String name, String parent, - String parentTag) { - if (parentTag.indexOf('.') != -1 || parentTag.equals(VIEW_TAG)) { - // Custom tag: We don't know whether it extends one of the builtin - // types where the layout param is valid, so don't complain - return true; - } - - SdkInfo sdk = context.getSdkInfo(); - - if (!parentTag.equals(parent)) { - String tag = sdk.getParentViewName(parentTag); - while (tag != null) { - if (tag.equals(parent)) { - return true; - } - tag = sdk.getParentViewName(tag); - } - - return false; - } - - return true; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/OnClickDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/OnClickDetector.java deleted file mode 100644 index 2a07a86..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/OnClickDetector.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * 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.ATTR_ON_CLICK; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; - -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.ClassScanner; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Location.Handle; -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 com.google.common.base.Joiner; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; -import org.w3c.dom.Attr; -import org.w3c.dom.Node; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Checks for missing onClick handlers - */ -public class OnClickDetector extends LayoutDetector implements ClassScanner { - /** Missing onClick handlers */ - public static final Issue ISSUE = Issue.create( - "OnClick", //$NON-NLS-1$ - "Ensures that onClick attribute values refer to real methods", - - "The `onClick` attribute value should be the name of a method in this View's context " + - "to invoke when the view is clicked. This name must correspond to a public method " + - "that takes exactly one parameter of type `View`.\n" + - "\n" + - "Must be a string value, using '\\;' to escape characters such as '\\n' or " + - "'\\uxxxx' for a unicode character.", - Category.CORRECTNESS, - 10, - Severity.ERROR, - OnClickDetector.class, - EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.CLASS_FILE)); - - private Map<String, Location.Handle> mNames; - private Map<String, List<String>> mSimilar; - private boolean mHaveBytecode; - - /** Constructs a new {@link OnClickDetector} */ - public OnClickDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (mNames != null && !mNames.isEmpty() && mHaveBytecode) { - List<String> names = new ArrayList<String>(mNames.keySet()); - Collections.sort(names); - LintDriver driver = context.getDriver(); - for (String name : names) { - Handle handle = mNames.get(name); - - Object clientData = handle.getClientData(); - if (clientData instanceof Node) { - if (driver.isSuppressed(ISSUE, (Node) clientData)) { - continue; - } - } - - Location location = handle.resolve(); - String message = String.format( - "Corresponding method handler 'public void %1$s(android.view.View)' not found", - name); - List<String> similar = mSimilar != null ? mSimilar.get(name) : null; - if (similar != null) { - Collections.sort(similar); - message += String.format(" (did you mean %1$s ?)", Joiner.on(", ").join(similar)); - } - context.report(ISSUE, location, message, null); - } - } - } - - // ---- Implements XmlScanner ---- - - @Override - public Collection<String> getApplicableAttributes() { - return Collections.singletonList(ATTR_ON_CLICK); - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - String value = attribute.getValue(); - if (value.isEmpty() || value.trim().isEmpty()) { - context.report(ISSUE, attribute, context.getLocation(attribute), - "onClick attribute value cannot be empty", null); - } else if (!value.equals(value.trim())) { - context.report(ISSUE, attribute, context.getLocation(attribute), - "There should be no whitespace around attribute values", null); - } else if (!value.startsWith(PREFIX_RESOURCE_REF)) { // Not resolved - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - if (mNames == null) { - mNames = new HashMap<String, Location.Handle>(); - } - Handle handle = context.parser.createLocationHandle(context, attribute); - handle.setClientData(attribute); - - // Replace unicode characters with the actual value since that's how they - // appear in the ASM signatures - if (value.contains("\\u")) { //$NON-NLS-1$ - Pattern pattern = Pattern.compile("\\\\u(\\d\\d\\d\\d)"); //$NON-NLS-1$ - Matcher matcher = pattern.matcher(value); - StringBuilder sb = new StringBuilder(value.length()); - int remainder = 0; - while (matcher.find()) { - sb.append(value.substring(0, matcher.start())); - String unicode = matcher.group(1); - int hex = Integer.parseInt(unicode, 16); - sb.append((char) hex); - remainder = matcher.end(); - } - sb.append(value.substring(remainder)); - value = sb.toString(); - } - - mNames.put(value, handle); - } - } - - // ---- Implements ClassScanner ---- - - @SuppressWarnings("rawtypes") - @Override - public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { - if (mNames == null) { - // No onClick attributes in the XML files - return; - } - - mHaveBytecode = true; - - List methodList = classNode.methods; - for (Object m : methodList) { - MethodNode method = (MethodNode) m; - boolean rightArguments = method.desc.equals("(Landroid/view/View;)V"); //$NON-NLS-1$ - if (!mNames.containsKey(method.name)) { - if (rightArguments) { - // See if there's a possible typo instead - for (String n : mNames.keySet()) { - if (LintUtils.editDistance(n, method.name) <= 2) { - recordSimilar(n, classNode, method); - break; - } - } - } - continue; - } - - // TODO: Validate class hierarchy: should extend a context method - // Longer term, also validate that it's in a layout that corresponds to - // the given activity - - if (rightArguments){ - // Found: remove from list to be checked - mNames.remove(method.name); - - // Make sure the method is public - if ((method.access & Opcodes.ACC_PUBLIC) == 0) { - Location location = context.getLocation(method, classNode); - String message = String.format( - "On click handler %1$s(View) must be public", - method.name); - context.report(ISSUE, location, message, null); - } else if ((method.access & Opcodes.ACC_STATIC) != 0) { - Location location = context.getLocation(method, classNode); - String message = String.format( - "On click handler %1$s(View) should not be static", - method.name); - context.report(ISSUE, location, message, null); - } - - if (mNames.isEmpty()) { - mNames = null; - return; - } - } - } - } - - private void recordSimilar(String name, ClassNode classNode, MethodNode method) { - if (mSimilar == null) { - mSimilar = new HashMap<String, List<String>>(); - } - List<String> list = mSimilar.get(name); - if (list == null) { - list = new ArrayList<String>(); - mSimilar.put(name, list); - } - - String signature = ClassContext.createSignature(classNode.name, method.name, method.desc); - list.add(signature); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/OverdrawDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/OverdrawDetector.java deleted file mode 100644 index 6627411..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/OverdrawDetector.java +++ /dev/null @@ -1,566 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_BACKGROUND; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_PARENT; -import static com.android.SdkConstants.ATTR_THEME; -import static com.android.SdkConstants.ATTR_TILE_MODE; -import static com.android.SdkConstants.DOT_JAVA; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.DRAWABLE_PREFIX; -import static com.android.SdkConstants.NULL_RESOURCE; -import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.TAG_ACTIVITY; -import static com.android.SdkConstants.TAG_APPLICATION; -import static com.android.SdkConstants.TAG_BITMAP; -import static com.android.SdkConstants.TAG_STYLE; -import static com.android.SdkConstants.TRANSPARENT_COLOR; -import static com.android.SdkConstants.VALUE_DISABLED; -import static com.android.tools.lint.detector.api.LintUtils.endsWith; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Project; -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 com.android.utils.Pair; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import lombok.ast.AstVisitor; -import lombok.ast.ClassDeclaration; -import lombok.ast.CompilationUnit; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.MethodInvocation; -import lombok.ast.PackageDeclaration; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.VariableReference; - -/** - * Check which looks for overdraw problems where view areas are painted and then - * painted over, meaning that the bottom paint operation is a waste of time. - */ -public class OverdrawDetector extends LayoutDetector implements Detector.JavaScanner { - private static final String R_STYLE_PREFIX = "R.style."; //$NON-NLS-1$ - private static final String SET_THEME = "setTheme"; //$NON-NLS-1$ - - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "Overdraw", //$NON-NLS-1$ - "Looks for overdraw issues (where a view is painted only to be fully painted over)", - "If you set a background drawable on a root view, then you should use a " + - "custom theme where the theme background is null. Otherwise, the theme background " + - "will be painted first, only to have your custom background completely cover it; " + - "this is called \"overdraw\".\n" + - "\n" + - "NOTE: This detector relies on figuring out which layouts are associated with " + - "which activities based on scanning the Java code, and it's currently doing that " + - "using an inexact pattern matching algorithm. Therefore, it can incorrectly " + - "conclude which activity the layout is associated with and then wrongly complain " + - "that a background-theme is hidden.\n" + - "\n" + - "If you want your custom background on multiple pages, then you should consider " + - "making a custom theme with your custom background and just using that theme " + - "instead of a root element background.\n" + - "\n" + - "Of course it's possible that your custom drawable is translucent and you want " + - "it to be mixed with the background. However, you will get better performance " + - "if you pre-mix the background with your drawable and use that resulting image or " + - "color as a custom theme background instead.\n", - - Category.PERFORMANCE, - 3, - Severity.WARNING, - OverdrawDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.JAVA_FILE, Scope.ALL_RESOURCE_FILES)); - - /** Mapping from FQN activity names to theme names registered in the manifest */ - private Map<String, String> mActivityToTheme; - - /** The default theme declared in the manifest, or null */ - private String mManifestTheme; - - /** Mapping from layout name (not including {@code @layout/} prefix) to activity FQN */ - private Map<String, List<String>> mLayoutToActivity; - - /** List of theme names registered in the project which have blank backgrounds */ - private List<String> mBlankThemes; - - /** Set of activities registered in the manifest. We will limit the Java analysis to - * these. */ - private Set<String> mActivities; - - /** List of drawable resources that are not flagged for overdraw (XML drawables - * except for {@code <bitmap>} drawables without tiling) */ - private List<String> mValidDrawables; - - /** - * List of pairs of (location, background drawable) corresponding to root elements - * in layouts that define a given background drawable. These should be checked to - * see if they are painting on top of a non-transparent theme. - */ - private List<Pair<Location, String>> mRootAttributes; - - /** Constructs a new {@link OverdrawDetector} */ - public OverdrawDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - // Look in layouts for drawable resources - return super.appliesTo(folderType) - // and in resource files for theme definitions - || folderType == ResourceFolderType.VALUES - // and in drawable files for bitmap tiling modes - || folderType == ResourceFolderType.DRAWABLE; - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return LintUtils.isXmlFile(file) || LintUtils.endsWith(file.getName(), DOT_JAVA); - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - /** Is the given theme a "blank" theme (one not painting its background) */ - private boolean isBlankTheme(String name) { - if (name.startsWith("@android:style/Theme_")) { //$NON-NLS-1$ - if (name.contains("NoFrame") //$NON-NLS-1$ - || name.contains("Theme_Wallpaper") //$NON-NLS-1$ - || name.contains("Theme_Holo_Wallpaper") //$NON-NLS-1$ - || name.contains("Theme_Translucent") //$NON-NLS-1$ - || name.contains("Theme_Dialog_NoFrame") //$NON-NLS-1$ - || name.contains("Theme_Holo_Dialog_Alert") //$NON-NLS-1$ - || name.contains("Theme_Holo_Light_Dialog_Alert") //$NON-NLS-1$ - || name.contains("Theme_Dialog_Alert") //$NON-NLS-1$ - || name.contains("Theme_Panel") //$NON-NLS-1$ - || name.contains("Theme_Light_Panel") //$NON-NLS-1$ - || name.contains("Theme_Holo_Panel") //$NON-NLS-1$ - || name.contains("Theme_Holo_Light_Panel")) { //$NON-NLS-1$ - return true; - } - } - - if (mBlankThemes != null && mBlankThemes.contains(name)) { - return true; - } - - return false; - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (mRootAttributes != null) { - for (Pair<Location, String> pair : mRootAttributes) { - Location location = pair.getFirst(); - - Object clientData = location.getClientData(); - if (clientData instanceof Node) { - if (context.getDriver().isSuppressed(ISSUE, (Node) clientData)) { - return; - } - } - - String layoutName = location.getFile().getName(); - if (endsWith(layoutName, DOT_XML)) { - layoutName = layoutName.substring(0, layoutName.length() - DOT_XML.length()); - } - - String theme = getTheme(context, layoutName); - if (theme == null || !isBlankTheme(theme)) { - String drawable = pair.getSecond(); - String message = String.format( - "Possible overdraw: Root element paints background %1$s with " + - "a theme that also paints a background (inferred theme is %2$s)", - drawable, theme); - // TODO: Compute applicable scope node - context.report(ISSUE, location, message, null); - } - - } - } - } - - /** Return the theme to be used for the given layout */ - private String getTheme(Context context, String layoutName) { - if (mActivityToTheme != null && mLayoutToActivity != null) { - List<String> activities = mLayoutToActivity.get(layoutName); - if (activities != null) { - for (String activity : activities) { - String theme = mActivityToTheme.get(activity); - if (theme != null) { - return theme; - } - } - } - } - - if (mManifestTheme != null) { - return mManifestTheme; - } - - Project project = context.getMainProject(); - int apiLevel = project.getTargetSdk(); - if (apiLevel == -1) { - apiLevel = project.getMinSdk(); - } - - if (apiLevel >= 11) { - return "@android:style/Theme.Holo"; //$NON-NLS-1$ - } else { - return "@android:style/Theme"; //$NON-NLS-1$ - } - } - - // ---- Implements XmlScanner ---- - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - // Only consider the root element's background - if (attribute.getOwnerDocument().getDocumentElement() == attribute.getOwnerElement()) { - // If the drawable is a non-repeated pattern then the overdraw might be - // intentional since the image isn't covering the whole screen - String background = attribute.getValue(); - if (mValidDrawables != null && mValidDrawables.contains(background)) { - return; - } - - if (background.equals(TRANSPARENT_COLOR)) { - return; - } - - if (background.startsWith("@android:drawable/")) { //$NON-NLS-1$ - // We haven't had a chance to study the builtin drawables the way we - // check the project local ones in scanBitmap() and beforeCheckFile(), - // but many of these are not bitmaps, so ignore these - return; - } - - String name = context.file.getName(); - if (name.contains("list_") || name.contains("_item")) { //$NON-NLS-1$ //$NON-NLS-2$ - // Canonical list_item layout name: don't warn about these, it's - // pretty common to want to paint custom list item backgrounds - return; - } - - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - Location location = context.getLocation(attribute); - location.setClientData(attribute); - if (mRootAttributes == null) { - mRootAttributes = new ArrayList<Pair<Location,String>>(); - } - mRootAttributes.add(Pair.of(location, attribute.getValue())); - } - } - - @Override - public Collection<String> getApplicableAttributes() { - return Collections.singletonList( - // Layouts: Look for background attributes on root elements for possible overdraw - ATTR_BACKGROUND - ); - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - // Manifest: Look at theme registrations - TAG_ACTIVITY, - TAG_APPLICATION, - - // Resource files: Look at theme definitions - TAG_STYLE, - - // Bitmaps - TAG_BITMAP - ); - } - - @Override - public void beforeCheckFile(@NonNull Context context) { - if (endsWith(context.file.getName(), DOT_XML)) { - // Drawable XML files should not be considered for overdraw, except for <bitmap>'s. - // The bitmap elements are handled in the scanBitmap() method; it will clear - // out anything added by this method. - File parent = context.file.getParentFile(); - ResourceFolderType type = ResourceFolderType.getFolderType(parent.getName()); - if (type == ResourceFolderType.DRAWABLE) { - if (mValidDrawables == null) { - mValidDrawables = new ArrayList<String>(); - } - String resource = getDrawableResource(context.file); - mValidDrawables.add(resource); - } - } - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String tag = element.getTagName(); - if (tag.equals(TAG_STYLE)) { - scanTheme(element); - } else if (tag.equals(TAG_ACTIVITY)) { - scanActivity(context, element); - } else if (tag.equals(TAG_APPLICATION)) { - if (element.hasAttributeNS(ANDROID_URI, ATTR_THEME)) { - mManifestTheme = element.getAttributeNS(ANDROID_URI, ATTR_THEME); - } - } else if (tag.equals(TAG_BITMAP)) { - scanBitmap(context, element); - } - } - - private static String getDrawableResource(File drawableFile) { - String resource = drawableFile.getName(); - if (endsWith(resource, DOT_XML)) { - resource = resource.substring(0, resource.length() - DOT_XML.length()); - } - return DRAWABLE_PREFIX + resource; - } - - private void scanBitmap(Context context, Element element) { - String tileMode = element.getAttributeNS(ANDROID_URI, ATTR_TILE_MODE); - if (!(tileMode.equals(VALUE_DISABLED) || tileMode.isEmpty())) { - if (mValidDrawables != null) { - String resource = getDrawableResource(context.file); - mValidDrawables.remove(resource); - } - } - } - - private void scanActivity(Context context, Element element) { - String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME); - if (name.indexOf('$') != -1) { - name = name.replace('$', '.'); - } - if (name.startsWith(".")) { //$NON-NLS-1$ - String pkg = context.getProject().getPackage(); - if (pkg != null && !pkg.isEmpty()) { - name = pkg + name; - } - } - - if (mActivities == null) { - mActivities = new HashSet<String>(); - } - mActivities.add(name); - - String theme = element.getAttributeNS(ANDROID_URI, ATTR_THEME); - if (theme != null && !theme.isEmpty()) { - if (mActivityToTheme == null) { - mActivityToTheme = new HashMap<String, String>(); - } - mActivityToTheme.put(name, theme.replace('.', '_')); - } - } - - private void scanTheme(Element element) { - // Look for theme definitions, and record themes that provide a null background. - String styleName = element.getAttribute(ATTR_NAME); - String parent = element.getAttribute(ATTR_PARENT); - if (parent == null) { - // Eclipse DOM workaround - parent = ""; - } - - if (parent.isEmpty()) { - int index = styleName.lastIndexOf('.'); - if (index != -1) { - parent = styleName.substring(0, index); - } - } - parent = parent.replace('.', '_'); - - String resource = STYLE_RESOURCE_PREFIX + styleName.replace('.', '_'); - - NodeList items = element.getChildNodes(); - for (int i = 0, n = items.getLength(); i < n; i++) { - if (items.item(i).getNodeType() == Node.ELEMENT_NODE) { - Element item = (Element) items.item(i); - String name = item.getAttribute(ATTR_NAME); - if (name.equals("android:windowBackground")) { //$NON-NLS-1$ - NodeList textNodes = item.getChildNodes(); - for (int j = 0, m = textNodes.getLength(); j < m; j++) { - Node textNode = textNodes.item(j); - if (textNode.getNodeType() == Node.TEXT_NODE) { - String text = textNode.getNodeValue(); - String trim = text.trim(); - if (!trim.isEmpty()) { - if (trim.equals(NULL_RESOURCE) - || trim.equals(TRANSPARENT_COLOR) - || mValidDrawables != null - && mValidDrawables.contains(trim)) { - if (mBlankThemes == null) { - mBlankThemes = new ArrayList<String>(); - } - mBlankThemes.add(resource); - } - } - } - } - - return; - } - } - } - - if (isBlankTheme(parent)) { - if (mBlankThemes == null) { - mBlankThemes = new ArrayList<String>(); - } - mBlankThemes.add(resource); - } - } - - // ---- Implements JavaScanner ---- - - @Override - public List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes() { - // This detector does not specify specific node types; this means - // that the infrastructure will run the full visitor on the compilation - // unit rather than on individual nodes. This is important since this - // detector relies on pruning (if it gets to a class declaration that is - // not an activity, it skips everything inside). - return null; - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - if (!context.getProject().getReportIssues()) { - return null; - } - return new OverdrawVisitor(); - } - - private class OverdrawVisitor extends ForwardingAstVisitor { - private static final String ACTIVITY = "Activity"; //$NON-NLS-1$ - private String mClassFqn; - - @Override - public boolean visitClassDeclaration(ClassDeclaration node) { - String name = node.getDescription(); - - if (mActivities != null && mActivities.contains(mClassFqn) || name.endsWith(ACTIVITY) - || node.astExtending() != null && - node.astExtending().getDescription().endsWith(ACTIVITY)) { - String packageName = ""; - if (node.getParent() instanceof CompilationUnit) { - CompilationUnit compilationUnit = (CompilationUnit) node.getParent(); - PackageDeclaration packageDeclaration = compilationUnit.astPackageDeclaration(); - if (packageDeclaration == null) { - // No package declaration: ignore this one - return true; - } - packageName = packageDeclaration.getPackageName(); - } - mClassFqn = (!packageName.isEmpty() ? (packageName + '.') : "") + name; - - return false; - } - - return true; // Done: No need to look inside this class - } - - // Store R.layout references in activity classes in a map mapping back layouts - // to activities - @Override - public boolean visitSelect(Select node) { - if (node.astIdentifier().astValue().equals("layout") //$NON-NLS-1$ - && node.astOperand() instanceof VariableReference - && ((VariableReference) node.astOperand()).astIdentifier().astValue() - .equals("R") //$NON-NLS-1$ - && node.getParent() instanceof Select) { - String layout = ((Select) node.getParent()).astIdentifier().astValue(); - if (mLayoutToActivity == null) { - mLayoutToActivity = new HashMap<String, List<String>>(); - } - List<String> list = mLayoutToActivity.get(layout); - if (list == null) { - list = new ArrayList<String>(); - mLayoutToActivity.put(layout, list); - } - list.add(mClassFqn); - } - - return false; - } - - - // Look for setTheme(R.style.whatever) and register as a theme registration - // for the current activity - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (node.astName().astValue().equals(SET_THEME)) { - // Look at argument - StrictListAccessor<Expression, MethodInvocation> args = node.astArguments(); - if (args.size() == 1) { - Expression arg = args.first(); - if (arg instanceof Select) { - String resource = arg.toString(); - if (resource.startsWith(R_STYLE_PREFIX)) { - if (mActivityToTheme == null) { - mActivityToTheme = new HashMap<String, String>(); - } - String name = ((Select) arg).astIdentifier().astValue(); - mActivityToTheme.put(mClassFqn, STYLE_RESOURCE_PREFIX + name); - } - } - } - } - - return false; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/OverrideDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/OverrideDetector.java deleted file mode 100644 index 15e2245..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/OverrideDetector.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * 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.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$ - "Looks for methods treated as overrides 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, - OverrideDetector.class, - EnumSet.of(Scope.ALL_CLASS_FILES)); - - /** map from owner class name to JVM signatures for its package private methods */ - private final Map<String, Set<String>> mPackagePrivateMethods = Maps.newHashMap(); - - /** Map from owner to signature to super class being overridden */ - private Map<String, Map<String, String>> 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<String, Map<String, Location>> 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<String> classes = mPackagePrivateMethods.keySet(); - LintDriver driver = context.getDriver(); - for (String owner : classes) { - Set<String> 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<String> superMethods = mPackagePrivateMethods.get(superClass); - if (superMethods != null) { - SetView<String> 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<String, Location> 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<String, String> 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.getScope()); - } - } else { - assert context.getPhase() == 2; - - for (Entry<String, Map<String, String>> ownerEntry : mErrors.entrySet()) { - String owner = ownerEntry.getKey(); - Map<String, String> methodToSuper = ownerEntry.getValue(); - for (Entry<String, String> entry : methodToSuper.entrySet()) { - String signature = entry.getKey(); - String superClass = entry.getValue(); - - Map<String, Location> ownerLocations = mLocations.get(owner); - if (ownerLocations != null) { - Location location = ownerLocations.get(signature); - if (location != null) { - Map<String, Location> 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, null); - } - } - } - } - } - } - - @SuppressWarnings("rawtypes") // ASM4 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<String> 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<String, Location> 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<String, String> 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); - } - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/PrivateKeyDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/PrivateKeyDetector.java deleted file mode 100644 index e8887a5..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/PrivateKeyDetector.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 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.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -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.base.Charsets; -import com.google.common.io.Files; - -import java.io.File; -import java.io.IOException; -import java.util.EnumSet; - -/** - * Looks for packaged private key files. - */ -public class PrivateKeyDetector extends Detector implements Detector.OtherFileScanner { - /** Packaged private key files */ - public static final Issue ISSUE = Issue.create( - "PackagedPrivateKey", //$NON-NLS-1$ - "Looks for packaged private key files", - - "In general, you should not package private key files inside your app.", - - Category.SECURITY, - 8, - Severity.WARNING, - PrivateKeyDetector.class, - Scope.OTHER_SCOPE); - - /** Constructs a new {@link PrivateKeyDetector} check */ - public PrivateKeyDetector() { - } - - private static boolean isPrivateKeyFile(File file) { - if (!file.isFile() || - (!LintUtils.endsWith(file.getPath(), "pem") && //NON-NLS-1$ - !LintUtils.endsWith(file.getPath(), "key"))) { //NON-NLS-1$ - return false; - } - - try { - String firstLine = Files.readFirstLine(file, Charsets.US_ASCII); - return firstLine != null && - firstLine.startsWith("---") && //NON-NLS-1$ - firstLine.contains("PRIVATE KEY"); //NON-NLS-1$ - } catch (IOException ex) { - // Don't care - } - - return false; - } - - // ---- Implements OtherFileScanner ---- - - @NonNull - @Override - public EnumSet<Scope> getApplicableFiles() { - return Scope.OTHER_SCOPE; - } - - @Override - public void run(@NonNull Context context) { - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - File file = context.file; - if (isPrivateKeyFile(file)) { - String fileName = file.getParentFile().getName() + File.separator - + file.getName(); - String message = String.format( - "The %1$s file seems to be a private key file. " + - "Please make sure not to embed this in your APK file.", fileName); - context.report(ISSUE, Location.create(file), message, null); - } - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.NORMAL; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java deleted file mode 100644 index d8a4ce0..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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.Attr; - -import java.util.Collection; - -/** - * Check which looks for access of private resources. - */ -public class PrivateResourceDetector extends ResourceXmlDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "PrivateResource", //$NON-NLS-1$ - "Looks for references to private resources", - "Private resources should not be referenced; the may not be present everywhere, and " + - "even where they are they may disappear without notice.\n" + - "\n" + - "To fix this, copy the resource into your own project. You can find the platform " + - "resources under `$ANDROID_SK/platforms/android-$VERSION/data/res/.`", - Category.CORRECTNESS, - 3, - Severity.FATAL, - PrivateResourceDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new detector */ - public PrivateResourceDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableAttributes() { - return ALL; - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - String value = attribute.getNodeValue(); - if (value.startsWith("@*android:")) { //$NON-NLS-1$ - context.report(ISSUE, attribute, context.getLocation(attribute), - "Illegal resource reference: @*android resources are private and " + - "not always present", null); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ProguardDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ProguardDetector.java deleted file mode 100644 index 7762659..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ProguardDetector.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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 static com.android.SdkConstants.PROGUARD_CONFIG; -import static com.android.SdkConstants.PROJECT_PROPERTIES; - -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.Detector; -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 java.io.File; -import java.util.EnumSet; - -/** - * Check which looks for errors in Proguard files. - */ -public class ProguardDetector extends Detector { - - /** The main issue discovered by this detector */ - public static final Issue WRONGKEEP = Issue.create( - "Proguard", //$NON-NLS-1$ - "Looks for problems in proguard config files", - "Using `-keepclasseswithmembernames` in a proguard config file is not " + - "correct; it can cause some symbols to be renamed which should not be.\n" + - "Earlier versions of ADT used to create proguard.cfg files with the " + - "wrong format. Instead of `-keepclasseswithmembernames` use " + - "`-keepclasseswithmembers`, since the old flags also implies " + - "\"allow shrinking\" which means symbols only referred to from XML and " + - "not Java (such as possibly CustomViews) can get deleted.", - Category.CORRECTNESS, - 8, - Severity.FATAL, - ProguardDetector.class, - EnumSet.of(Scope.PROGUARD_FILE)).setMoreInfo( - "http://http://code.google.com/p/android/issues/detail?id=16384"); //$NON-NLS-1$ - - /** Finds ProGuard files that contain non-project specific configuration - * locally and suggests replacing it with an include path */ - public static final Issue SPLITCONFIG = Issue.create( - "ProguardSplit", //$NON-NLS-1$ - "Checks for old proguard.cfg files that contain generic Android rules", - - "Earlier versions of the Android tools bundled a single `proguard.cfg` file " + - "containing a ProGuard configuration file suitable for Android shrinking and " + - "obfuscation. However, that version was copied into new projects, which " + - "means that it does not continue to get updated as we improve the default " + - "ProGuard rules for Android.\n" + - "\n" + - "In the new version of the tools, we have split the ProGuard configuration " + - "into two halves:\n" + - "* A simple configuration file containing only project-specific flags, in " + - "your project\n" + - "* A generic configuration file containing the recommended set of ProGuard " + - "options for Android projects. This generic file lives in the SDK install " + - "directory which means that it gets updated along with the tools.\n" + - "\n" + - "In order for this to work, the proguard.config property in the " + - "`project.properties` file now refers to a path, so you can reference both " + - "the generic file as well as your own (and any additional files too).\n" + - "\n" + - "To migrate your project to the new setup, create a new `proguard-project.txt` file " + - "in your project containing any project specific ProGuard flags as well as " + - "any customizations you have made, then update your project.properties file " + - "to contain:\n" + - "`proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt`", - - Category.CORRECTNESS, - 3, - Severity.WARNING, - ProguardDetector.class, - EnumSet.of(Scope.PROGUARD_FILE)); - - @Override - public void run(@NonNull Context context) { - String contents = context.getContents(); - if (contents != null) { - if (context.isEnabled(WRONGKEEP)) { - int index = contents.indexOf( - // Old pattern: - "-keepclasseswithmembernames class * {\n" + //$NON-NLS-1$ - " public <init>(android."); //$NON-NLS-1$ - if (index != -1) { - context.report(WRONGKEEP, - Location.create(context.file, contents, index, index), - "Obsolete ProGuard file; use -keepclasseswithmembers instead of " + - "-keepclasseswithmembernames", null); - } - } - if (context.isEnabled(SPLITCONFIG)) { - int index = contents.indexOf("-keep public class * extends android.app.Activity"); - if (index != -1) { - // Only complain if project.properties actually references this file; - // no need to bother the users who got a default proguard.cfg file - // when they created their projects but haven't actually hooked it up - // to shrinking & obfuscation. - File propertyFile = new File(context.file.getParentFile(), PROJECT_PROPERTIES); - if (!propertyFile.exists()) { - return; - } - String properties = context.getClient().readFile(propertyFile); - int i = properties.indexOf(PROGUARD_CONFIG); - if (i == -1) { - return; - } - // Make sure the entry isn't just commented out, such as - // # To enable ProGuard to shrink and obfuscate your code, uncomment this: - // #proguard.config=proguard.cfg - for (; i >= 0; i--) { - char c = properties.charAt(i); - if (c == '#') { - return; - } - if (c == '\n') { - break; - } - } - if (properties.contains(PROGUARD_CONFIG)) { - context.report(SPLITCONFIG, - Location.create(context.file, contents, index, index), - String.format( - "Local ProGuard configuration contains general Android " + - "configuration: Inherit these settings instead? " + - "Modify project.properties to define " + - "proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:%1$s" + - " and then keep only project-specific configuration here", - context.file.getName()), null); - } - } - } - } - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/PxUsageDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/PxUsageDetector.java deleted file mode 100644 index ed9447e..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/PxUsageDetector.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_TEXT_SIZE; -import static com.android.SdkConstants.TAG_ITEM; -import static com.android.SdkConstants.TAG_STYLE; -import static com.android.SdkConstants.UNIT_DIP; -import static com.android.SdkConstants.UNIT_DP; -import static com.android.SdkConstants.UNIT_IN; -import static com.android.SdkConstants.UNIT_MM; -import static com.android.SdkConstants.UNIT_PX; -import static com.android.SdkConstants.UNIT_SP; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -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.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.Collection; -import java.util.Collections; - -/** - * Check for px dimensions instead of dp dimensions. - * Also look for non-"sp" text sizes. - */ -public class PxUsageDetector extends LayoutDetector { - /** Using px instead of dp */ - public static final Issue PX_ISSUE = Issue.create( - "PxUsage", //$NON-NLS-1$ - "Looks for use of the \"px\" dimension", - // This description is from the below screen support document - "For performance reasons and to keep the code simpler, the Android system uses pixels " + - "as the standard unit for expressing dimension or coordinate values. That means that " + - "the dimensions of a view are always expressed in the code using pixels, but " + - "always based on the current screen density. For instance, if `myView.getWidth()` " + - "returns 10, the view is 10 pixels wide on the current screen, but on a device with " + - "a higher density screen, the value returned might be 15. If you use pixel values " + - "in your application code to work with bitmaps that are not pre-scaled for the " + - "current screen density, you might need to scale the pixel values that you use in " + - "your code to match the un-scaled bitmap source.", - Category.CORRECTNESS, - 2, - Severity.WARNING, - PxUsageDetector.class, - Scope.RESOURCE_FILE_SCOPE).setMoreInfo( - "http://developer.android.com/guide/practices/screens_support.html#screen-independence"); //$NON-NLS-1$ - - /** Using mm/in instead of dp */ - public static final Issue IN_MM_ISSUE = Issue.create( - "InOrMmUsage", //$NON-NLS-1$ - "Looks for use of the \"mm\" or \"in\" dimensions", - - "Avoid using `mm` (millimeters) or `in` (inches) as the unit for dimensions.\n" + - "\n" + - "While it should work in principle, unfortunately many devices do not report " + - "the correct true physical density, which means that the dimension calculations " + - "won't work correctly. You are better off using `dp` (and for font sizes, `sp`.)", - - Category.CORRECTNESS, - 4, - Severity.WARNING, - PxUsageDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Using sp instead of dp */ - public static final Issue DP_ISSUE = Issue.create( - "SpUsage", //$NON-NLS-1$ - "Looks for uses of \"dp\" instead of \"sp\" dimensions for text sizes", - - "When setting text sizes, you should normally use `sp`, or \"scale-independent " + - "pixels\". This is like the `dp` unit, but it is also scaled " + - "by the user's font size preference. It is recommend you use this unit when " + - "specifying font sizes, so they will be adjusted for both the screen density " + - "and the user's preference.\n" + - "\n" + - "There *are* cases where you might need to use `dp`; typically this happens when " + - "the text is in a container with a specific dp-size. This will prevent the text " + - "from spilling outside the container. Note however that this means that the user's " + - "font size settings are not respected, so consider adjusting the layout itself " + - "to be more flexible.", - Category.CORRECTNESS, - 3, - Severity.WARNING, - PxUsageDetector.class, - Scope.RESOURCE_FILE_SCOPE).setMoreInfo( - "http://developer.android.com/training/multiscreen/screendensities.html"); //$NON-NLS-1$ - - /** Using text sizes that are too small */ - public static final Issue SMALL_SP_ISSUE = Issue.create( - "SmallSp", //$NON-NLS-1$ - "Looks for text sizes that are too small", - - "Avoid using sizes smaller than 12sp.", - - Category.USABILITY, - 4, - Severity.WARNING, - PxUsageDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - - /** Constructs a new {@link PxUsageDetector} */ - public PxUsageDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - // Look in both layouts (at attribute values) and in value files (at style definitions) - return folderType == ResourceFolderType.LAYOUT || folderType == ResourceFolderType.VALUES; - } - - @Override - public Collection<String> getApplicableAttributes() { - return ALL; - } - - @Override - @Nullable - public Collection<String> getApplicableElements() { - return Collections.singletonList(TAG_STYLE); - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - if (context.getResourceFolderType() != ResourceFolderType.LAYOUT) { - return; - } - - String value = attribute.getValue(); - if (value.endsWith(UNIT_PX) && value.matches("\\d+px")) { //$NON-NLS-1$ - if (value.charAt(0) == '0') { - // 0px is fine. 0px is 0dp regardless of density... - return; - } - if (context.isEnabled(PX_ISSUE)) { - context.report(PX_ISSUE, attribute, context.getLocation(attribute), - "Avoid using \"px\" as units; use \"dp\" instead", null); - } - } else if (value.endsWith(UNIT_MM) && value.matches("\\d+mm") //$NON-NLS-1$ - || value.endsWith(UNIT_IN) && value.matches("\\d+in")) { //$NON-NLS-1$ - if (value.charAt(0) == '0') { - // 0mm == 0in == 0dp - return; - } - if (context.isEnabled(IN_MM_ISSUE)) { - String unit = value.substring(value.length() - 2); - context.report(IN_MM_ISSUE, attribute, context.getLocation(attribute), - String.format("Avoid using \"%1$s\" as units " + - "(it does not work accurately on all devices); use \"dp\" instead", - unit), - null); - } - } else if (value.endsWith(UNIT_SP) - && (ATTR_TEXT_SIZE.equals(attribute.getLocalName()) - || ATTR_LAYOUT_HEIGHT.equals(attribute.getLocalName())) - && value.matches("\\d+sp")) { //$NON-NLS-1$ - int size = getSize(value); - if (size > 0 && size < 12) { - context.report(SMALL_SP_ISSUE, attribute, context.getLocation(attribute), - String.format("Avoid using sizes smaller than 12sp: %1$s", value), - null); - } - } else if (ATTR_TEXT_SIZE.equals(attribute.getLocalName()) - && (value.endsWith(UNIT_DP) || value.endsWith(UNIT_DIP)) - && (value.matches("\\d+di?p"))) { //$NON-NLS-1$ - if (context.isEnabled(DP_ISSUE)) { - context.report(DP_ISSUE, attribute, context.getLocation(attribute), - "Should use \"sp\" instead of \"dp\" for text sizes", null); - } - } - } - - private static int getSize(String text) { - assert text.matches("\\d+sp") : text; //$NON-NLS-1$ - return Integer.parseInt(text.substring(0, text.length() - 2)); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (context.getResourceFolderType() != ResourceFolderType.VALUES) { - return; - } - - assert element.getTagName().equals(TAG_STYLE); - NodeList itemNodes = element.getChildNodes(); - for (int j = 0, nodeCount = itemNodes.getLength(); j < nodeCount; j++) { - Node item = itemNodes.item(j); - if (item.getNodeType() == Node.ELEMENT_NODE && - TAG_ITEM.equals(item.getNodeName())) { - Element itemElement = (Element) item; - NodeList childNodes = item.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() != Node.TEXT_NODE) { - return; - } - - checkStyleItem(context, itemElement, child); - } - } - } - } - - private static void checkStyleItem(XmlContext context, Element item, Node textNode) { - String text = textNode.getNodeValue(); - for (int j = text.length() - 1; j > 0; j--) { - char c = text.charAt(j); - if (!Character.isWhitespace(c)) { - if (c == 'x' && text.charAt(j - 1) == 'p') { // ends with px - text = text.trim(); - if (text.matches("\\d+px") && text.charAt(0) != '0') { //$NON-NLS-1$ - if (context.isEnabled(PX_ISSUE)) { - context.report(PX_ISSUE, item, context.getLocation(textNode), - "Avoid using \"px\" as units; use \"dp\" instead", null); - } - } - } else if (c == 'm' && text.charAt(j - 1) == 'm' || - c == 'n' && text.charAt(j - 1) == 'i') { - text = text.trim(); - String unit = text.substring(text.length() - 2); - if (text.matches("\\d+" + unit) && text.charAt(0) != '0') { //$NON-NLS-1$ - if (context.isEnabled(IN_MM_ISSUE)) { - context.report(IN_MM_ISSUE, item, context.getLocation(textNode), - String.format("Avoid using \"%1$s\" as units " - + "(it does not work accurately on all devices); " - + "use \"dp\" instead", unit), null); - } - } - } else if (c == 'p' && (text.charAt(j - 1) == 'd' - || text.charAt(j - 1) == 'i')) { // ends with dp or di - text = text.trim(); - String name = item.getAttribute(ATTR_NAME); - if ((name.equals(ATTR_TEXT_SIZE) - || name.equals("android:textSize")) //$NON-NLS-1$ - && text.matches("\\d+di?p")) { //$NON-NLS-1$ - if (context.isEnabled(DP_ISSUE)) { - context.report(DP_ISSUE, item, context.getLocation(textNode), - "Should use \"sp\" instead of \"dp\" for text sizes", null); - } - } - } else if (c == 'p' && text.charAt(j - 1) == 's') { - String name = item.getAttribute(ATTR_NAME); - if (ATTR_TEXT_SIZE.equals(name) || ATTR_LAYOUT_HEIGHT.equals(name)) { - text = text.trim(); - String unit = text.substring(text.length() - 2); - if (text.matches("\\d+" + unit)) { //$NON-NLS-1$ - if (context.isEnabled(SMALL_SP_ISSUE)) { - int size = getSize(text); - if (size > 0 && size < 12) { - context.report(SMALL_SP_ISSUE, item, - context.getLocation(textNode), String.format( - "Avoid using sizes smaller than 12sp: %1$s", - text), null); - } - } - } - } - } - break; - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java deleted file mode 100644 index c4fd0a7..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * 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.ANDROID_APP_ACTIVITY; -import static com.android.SdkConstants.ANDROID_APP_SERVICE; -import static com.android.SdkConstants.ANDROID_CONTENT_BROADCAST_RECEIVER; -import static com.android.SdkConstants.ANDROID_CONTENT_CONTENT_PROVIDER; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_PACKAGE; -import static com.android.SdkConstants.TAG_ACTIVITY; -import static com.android.SdkConstants.TAG_PROVIDER; -import static com.android.SdkConstants.TAG_RECEIVER; -import static com.android.SdkConstants.TAG_SERVICE; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -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.android.tools.lint.detector.api.XmlContext; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.w3c.dom.Element; - -import java.util.Arrays; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Map.Entry; - -/** - * Checks for missing manifest registrations for activities, services etc - * and also makes sure that they are registered with the correct tag - */ -public class RegistrationDetector extends LayoutDetector implements ClassScanner { - /** Unregistered activities and services */ - public static final Issue ISSUE = Issue.create( - "Registered", //$NON-NLS-1$ - "Ensures that Activities, Services and Content Providers are registered in the manifest", - - "Activities, services and content providers should be registered in the " + - "`AndroidManifest.xml` file using `<activity>`, `<service>` and `<provider>` tags.\n" + - "\n" + - "If your activity is simply a parent class intended to be subclassed by other " + - "\"real\" activities, make it an abstract class.", - - Category.CORRECTNESS, - 6, - Severity.WARNING, - RegistrationDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.CLASS_FILE)).setMoreInfo( - "http://developer.android.com/guide/topics/manifest/manifest-intro.html"); //$NON-NLS-1$ - - private Multimap<String, String> mManifestRegistrations; - - /** Constructs a new {@link RegistrationDetector} */ - public RegistrationDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements XmlScanner ---- - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList(sTags); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String fqcn = getFqcn(element); - String tag = element.getTagName(); - String frameworkClass = tagToClass(tag); - if (frameworkClass != null) { - String signature = ClassContext.getInternalName(fqcn); - if (mManifestRegistrations == null) { - mManifestRegistrations = ArrayListMultimap.create(4, 8); - } - mManifestRegistrations.put(frameworkClass, signature); - if (signature.indexOf('$') != -1) { - // The internal name contains a $ which means it's an inner class. - // The conversion from fqcn to internal name is a bit ambiguous: - // "a.b.C.D" usually means "inner class D in class C in package a.b". - // However, it can (see issue 31592) also mean class D in package "a.b.C". - // Place *both* of these possibilities in the registered map, since this - // is only used to check that an activity is registered, not the other way - // (so it's okay to have entries there that do not correspond to real classes). - signature = signature.replace('$', '/'); - mManifestRegistrations.put(frameworkClass, signature); - } - } - } - - /** - * Returns the fully qualified class name for a manifest entry element that - * specifies a name attribute - * - * @param element the element - * @return the fully qualified class name - */ - @NonNull - private static String getFqcn(@NonNull Element element) { - Element root = element.getOwnerDocument().getDocumentElement(); - String pkg = root.getAttribute(ATTR_PACKAGE); - String className = element.getAttributeNS(ANDROID_URI, ATTR_NAME); - if (className.startsWith(".")) { //$NON-NLS-1$ - return pkg + className; - } else if (className.indexOf('.') == -1) { - // According to the <activity> manifest element documentation, this is not - // valid ( http://developer.android.com/guide/topics/manifest/activity-element.html ) - // but it appears in manifest files and appears to be supported by the runtime - // so handle this in code as well: - return pkg + '.' + className; - } // else: the class name is already a fully qualified class name - - return className; - } - - // ---- Implements ClassScanner ---- - - @Override - public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { - // Abstract classes do not need to be registered - if ((classNode.access & Opcodes.ACC_ABSTRACT) != 0) { - return; - } - String curr = classNode.name; - - int lastIndex = curr.lastIndexOf('$'); - if (lastIndex != -1 && lastIndex < curr.length() - 1) { - if (Character.isDigit(curr.charAt(lastIndex+1))) { - // Anonymous inner class, doesn't need to be registered - return; - } - } - - while (curr != null) { - for (String s : sClasses) { - if (curr.equals(s)) { - Collection<String> registered = mManifestRegistrations != null ? - mManifestRegistrations.get(curr) : null; - if (registered == null || !registered.contains(classNode.name)) { - report(context, classNode, curr); - } - - } - } - - curr = context.getDriver().getSuperClass(curr); - } - } - - private void report(ClassContext context, ClassNode classNode, String curr) { - String tag = classToTag(curr); - String className = ClassContext.createSignature(classNode.name, null, null); - - String wrongClass = null; // The framework class this class actually extends - if (mManifestRegistrations != null) { - Collection<Entry<String,String>> entries = - mManifestRegistrations.entries(); - for (Entry<String,String> entry : entries) { - if (entry.getValue().equals(classNode.name)) { - wrongClass = entry.getKey(); - break; - } - } - } - if (wrongClass != null) { - Location location = context.getLocation(classNode); - context.report( - ISSUE, - location, - String.format( - "%1$s is a <%2$s> but is registered in the manifest as a <%3$s>", - className, tag, classToTag(wrongClass)), - null); - } else if (!tag.equals(TAG_RECEIVER)) { // don't need to be registered - Location location = context.getLocation(classNode); - context.report( - ISSUE, - location, - String.format( - "The <%1$s> %2$s is not registered in the manifest", - tag, className), - null); - } - } - - /** The manifest tags we care about */ - private static final String[] sTags = new String[] { - TAG_ACTIVITY, - TAG_SERVICE, - TAG_RECEIVER, - TAG_PROVIDER, - // Keep synchronized with {@link #sClasses} - }; - - /** The corresponding framework classes that the tags in {@link #sTags} should extend */ - private static final String[] sClasses = new String[] { - ANDROID_APP_ACTIVITY, - ANDROID_APP_SERVICE, - ANDROID_CONTENT_BROADCAST_RECEIVER, - ANDROID_CONTENT_CONTENT_PROVIDER, - // Keep synchronized with {@link #sTags} - }; - - /** Looks up the corresponding framework class a given manifest tag's class should extend */ - private static String tagToClass(String tag) { - for (int i = 0, n = sTags.length; i < n; i++) { - if (sTags[i].equals(tag)) { - return sClasses[i]; - } - } - - return null; - } - - /** Looks up the tag a given framework class should be registered with */ - private static String classToTag(String className) { - for (int i = 0, n = sClasses.length; i < n; i++) { - if (sClasses[i].equals(className)) { - return sTags[i]; - } - } - - return null; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java deleted file mode 100644 index 861c1be..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java +++ /dev/null @@ -1,618 +0,0 @@ -/* - * 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.ANDROID_NS_NAME_PREFIX; -import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_LAYOUT; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_PARENT; -import static com.android.SdkConstants.ATTR_STYLE; -import static com.android.SdkConstants.FD_RES_LAYOUT; -import static com.android.SdkConstants.FN_RESOURCE_BASE; -import static com.android.SdkConstants.FQCN_GRID_LAYOUT_V7; -import static com.android.SdkConstants.GRID_LAYOUT; -import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.REQUEST_FOCUS; -import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.TABLE_LAYOUT; -import static com.android.SdkConstants.TABLE_ROW; -import static com.android.SdkConstants.TAG_ITEM; -import static com.android.SdkConstants.TAG_STYLE; -import static com.android.SdkConstants.VIEW_INCLUDE; -import static com.android.SdkConstants.VIEW_MERGE; -import static com.android.resources.ResourceFolderType.LAYOUT; -import static com.android.resources.ResourceFolderType.VALUES; -import static com.android.tools.lint.detector.api.LintUtils.getLayoutName; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -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 com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.File; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.MethodInvocation; -import lombok.ast.NullLiteral; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.VariableReference; - -/** - * Ensures that layout width and height attributes are specified - */ -public class RequiredAttributeDetector extends LayoutDetector implements Detector.JavaScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "RequiredSize", //$NON-NLS-1$ - "Ensures that the layout_width and layout_height are specified for all views", - - "All views must specify an explicit layout_width and layout_height attribute. " + - "There is a runtime check for this, so if you fail to specify a size, an exception " + - "is thrown at runtime.\n" + - "\n" + - "It's possible to specify these widths via styles as well. GridLayout, as a special " + - "case, does not require you to specify a size.", - Category.CORRECTNESS, - 4, - Severity.ERROR, - RequiredAttributeDetector.class, - EnumSet.of(Scope.JAVA_FILE, Scope.ALL_RESOURCE_FILES)); - - /** Map from each style name to parent style */ - @Nullable private Map<String, String> mStyleParents; - - /** Set of style names where the style sets the layout width */ - @Nullable private Set<String> mWidthStyles; - - /** Set of style names where the style sets the layout height */ - @Nullable private Set<String> mHeightStyles; - - /** Set of layout names for layouts that are included by an {@code <include>} tag - * where the width is set on the include */ - @Nullable private Set<String> mIncludedWidths; - - /** Set of layout names for layouts that are included by an {@code <include>} tag - * where the height is set on the include */ - @Nullable private Set<String> mIncludedHeights; - - /** Set of layout names for layouts that are included by an {@code <include>} tag - * where the width is <b>not</b> set on the include */ - @Nullable private Set<String> mNotIncludedWidths; - - /** Set of layout names for layouts that are included by an {@code <include>} tag - * where the height is <b>not</b> set on the include */ - @Nullable private Set<String> mNotIncludedHeights; - - /** Whether the width was set in a theme definition */ - private boolean mSetWidthInTheme; - - /** Whether the height was set in a theme definition */ - private boolean mSetHeightInTheme; - - /** Constructs a new {@link RequiredAttributeDetector} */ - public RequiredAttributeDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == LAYOUT || folderType == VALUES; - } - - @Override - public void afterCheckProject(@NonNull Context context) { - // Process checks in two phases: - // Phase 1: Gather styles and includes (styles are encountered after the layouts - // so we can't do it in a single phase, and includes can be affected by includes from - // layouts we haven't seen yet) - // Phase 2: Process layouts, using gathered style and include data, and mark layouts - // not known. - // - if (context.getPhase() == 1) { - checkSizeSetInTheme(); - - context.requestRepeat(this, Scope.RESOURCE_FILE_SCOPE); - } - } - - private boolean isWidthStyle(String style) { - return isSizeStyle(style, mWidthStyles); - } - - private boolean isHeightStyle(String style) { - return isSizeStyle(style, mHeightStyles); - } - - private boolean isSizeStyle(String style, Set<String> sizeStyles) { - if (isFrameworkSizeStyle(style)) { - return true; - } - if (sizeStyles == null) { - return false; - } - return isSizeStyle(stripStylePrefix(style), sizeStyles, 0); - } - - private static boolean isFrameworkSizeStyle(String style) { - // The styles Widget.TextView.ListSeparator (and several theme variations, such as - // Widget.Holo.TextView.ListSeparator, Widget.Holo.Light.TextView.ListSeparator, etc) - // define layout_width and layout_height. - // These are exposed through the listSeparatorTextViewStyle style. - if (style.equals("?android:attr/listSeparatorTextViewStyle") //$NON-NLS-1$ - || style.equals("?android/listSeparatorTextViewStyle")) { //$NON-NLS-1$ - return true; - } - - // It's also set on Widget.QuickContactBadge and Widget.QuickContactBadgeSmall - // These are exposed via a handful of attributes with a common prefix - if (style.startsWith("?android:attr/quickContactBadgeStyle")) { //$NON-NLS-1$ - return true; - } - - // Finally, the styles are set on MediaButton and Widget.Holo.Tab (and - // Widget.Holo.Light.Tab) but these are not exposed via attributes. - - return false; - } - - private boolean isSizeStyle( - @NonNull String style, - @NonNull Set<String> sizeStyles, int depth) { - if (depth == 30) { - // Cycle between local and framework attribute style missed - // by the fact that we're stripping the distinction between framework - // and local styles here - return false; - } - - assert !style.startsWith(STYLE_RESOURCE_PREFIX) - && !style.startsWith(ANDROID_STYLE_RESOURCE_PREFIX); - - if (sizeStyles.contains(style)) { - return true; - } - - if (mStyleParents != null) { - String parentStyle = mStyleParents.get(style); - if (parentStyle != null) { - parentStyle = stripStylePrefix(parentStyle); - if (isSizeStyle(parentStyle, sizeStyles, depth + 1)) { - return true; - } - } - } - - int index = style.lastIndexOf('.'); - if (index > 0) { - return isSizeStyle(style.substring(0, index), sizeStyles, depth + 1); - } - - return false; - } - - private void checkSizeSetInTheme() { - // Look through the styles and determine whether each style is a theme - if (mStyleParents == null) { - return; - } - - Map<String, Boolean> isTheme = Maps.newHashMap(); - for (String style : mStyleParents.keySet()) { - if (isTheme(stripStylePrefix(style), isTheme, 0)) { - mSetWidthInTheme = true; - mSetHeightInTheme = true; - break; - } - } - } - - private boolean isTheme(String style, Map<String, Boolean> isTheme, int depth) { - if (depth == 30) { - // Cycle between local and framework attribute style missed - // by the fact that we're stripping the distinction between framework - // and local styles here - return false; - } - - assert !style.startsWith(STYLE_RESOURCE_PREFIX) - && !style.startsWith(ANDROID_STYLE_RESOURCE_PREFIX); - - Boolean known = isTheme.get(style); - if (known != null) { - return known; - } - - if (style.contains("Theme")) { //$NON-NLS-1$ - isTheme.put(style, true); - return true; - } - - if (mStyleParents != null) { - String parentStyle = mStyleParents.get(style); - if (parentStyle != null) { - parentStyle = stripStylePrefix(parentStyle); - if (isTheme(parentStyle, isTheme, depth + 1)) { - isTheme.put(style, true); - return true; - } - } - } - - int index = style.lastIndexOf('.'); - if (index > 0) { - String parentStyle = style.substring(0, index); - boolean result = isTheme(parentStyle, isTheme, depth + 1); - isTheme.put(style, result); - return result; - } - - return false; - } - - private static boolean hasLayoutVariations(File file) { - File parent = file.getParentFile(); - if (parent == null) { - return false; - } - File res = file.getParentFile(); - if (res == null) { - return false; - } - String name = file.getName(); - File[] folders = res.listFiles(); - if (folders == null) { - return false; - } - for (File folder : folders) { - if (!folder.getName().startsWith(FD_RES_LAYOUT)) { - continue; - } - if (folder.equals(parent)) { - continue; - } - File other = new File(folder, name); - if (other.exists()) { - return true; - } - } - - return false; - } - - private static String stripStylePrefix(@NonNull String style) { - if (style.startsWith(STYLE_RESOURCE_PREFIX)) { - style = style.substring(STYLE_RESOURCE_PREFIX.length()); - } else if (style.startsWith(ANDROID_STYLE_RESOURCE_PREFIX)) { - style = style.substring(ANDROID_STYLE_RESOURCE_PREFIX.length()); - } - - return style; - } - - private static boolean isRootElement(@NonNull Node node) { - return node == node.getOwnerDocument().getDocumentElement(); - } - - // ---- Implements XmlScanner ---- - - @Override - public Collection<String> getApplicableElements() { - return ALL; - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - ResourceFolderType folderType = context.getResourceFolderType(); - int phase = context.getPhase(); - if (phase == 1 && folderType == VALUES) { - String tag = element.getTagName(); - if (TAG_STYLE.equals(tag)) { - String parent = element.getAttribute(ATTR_PARENT); - if (parent != null && !parent.isEmpty()) { - String name = element.getAttribute(ATTR_NAME); - if (name != null && !name.isEmpty()) { - if (mStyleParents == null) { - mStyleParents = Maps.newHashMap(); - } - mStyleParents.put(name, parent); - } - } - } else if (TAG_ITEM.equals(tag) - && TAG_STYLE.equals(element.getParentNode().getNodeName())) { - String name = element.getAttribute(ATTR_NAME); - if (name.endsWith(ATTR_LAYOUT_WIDTH) && - name.equals(ANDROID_NS_NAME_PREFIX + ATTR_LAYOUT_WIDTH)) { - if (mWidthStyles == null) { - mWidthStyles = Sets.newHashSet(); - } - String styleName = ((Element) element.getParentNode()).getAttribute(ATTR_NAME); - mWidthStyles.add(styleName); - } - if (name.endsWith(ATTR_LAYOUT_HEIGHT) && - name.equals(ANDROID_NS_NAME_PREFIX + ATTR_LAYOUT_HEIGHT)) { - if (mHeightStyles == null) { - mHeightStyles = Sets.newHashSet(); - } - String styleName = ((Element) element.getParentNode()).getAttribute(ATTR_NAME); - mHeightStyles.add(styleName); - } - } - } else if (folderType == LAYOUT) { - if (phase == 1) { - // Gather includes - if (element.getTagName().equals(VIEW_INCLUDE)) { - String layout = element.getAttribute(ATTR_LAYOUT); - if (layout != null && !layout.isEmpty()) { - recordIncludeWidth(layout, - element.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH)); - recordIncludeHeight(layout, - element.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT)); - } - } - } else { - assert phase == 2; // Check everything using style data and include data - boolean hasWidth = element.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH); - boolean hasHeight = element.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT); - - if (mSetWidthInTheme) { - hasWidth = true; - } - - if (mSetHeightInTheme) { - hasHeight = true; - } - - if (hasWidth && hasHeight) { - return; - } - - String tag = element.getTagName(); - if (VIEW_MERGE.equals(tag) - || VIEW_INCLUDE.equals(tag) - || REQUEST_FOCUS.equals(tag)) { - return; - } - - String parentTag = element.getParentNode() != null - ? element.getParentNode().getNodeName() : ""; - if (TABLE_LAYOUT.equals(parentTag) - || TABLE_ROW.equals(parentTag) - || GRID_LAYOUT.equals(parentTag) - || FQCN_GRID_LAYOUT_V7.equals(parentTag)) { - return; - } - - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - boolean certain = true; - boolean isRoot = isRootElement(element); - if (isRoot || isRootElement(element.getParentNode()) - && VIEW_MERGE.equals(parentTag)) { - String name = LAYOUT_RESOURCE_PREFIX + getLayoutName(context.file); - if (!hasWidth && mIncludedWidths != null) { - hasWidth = mIncludedWidths.contains(name); - // If the layout is *also* included in a context where the width - // was not set, we're not certain; it's possible that - if (mNotIncludedWidths != null && mNotIncludedWidths.contains(name)) { - hasWidth = false; - // If we only have a single layout we know that this layout isn't - // always included with layout_width or layout_height set, but - // if there are multiple layouts, it's possible that at runtime - // we only load the size-less layout by the tag which includes - // the size - certain = !hasLayoutVariations(context.file); - } - } - if (!hasHeight && mIncludedHeights != null) { - hasHeight = mIncludedHeights.contains(name); - if (mNotIncludedHeights != null && mNotIncludedHeights.contains(name)) { - hasHeight = false; - certain = !hasLayoutVariations(context.file); - } - } - if (hasWidth && hasHeight) { - return; - } - } - - if (!hasWidth || !hasHeight) { - String style = element.getAttribute(ATTR_STYLE); - if (style != null && !style.isEmpty()) { - if (!hasWidth) { - hasWidth = isWidthStyle(style); - } - if (!hasHeight) { - hasHeight = isHeightStyle(style); - } - } - if (hasWidth && hasHeight) { - return; - } - } - - String message; - if (!(hasWidth || hasHeight)) { - if (certain) { - message = "The required layout_width and layout_height attributes " + - "are missing"; - } else { - message = "The required layout_width and layout_height attributes " + - "*may* be missing"; - } - } else { - String attribute = hasWidth ? ATTR_LAYOUT_HEIGHT : ATTR_LAYOUT_WIDTH; - if (certain) { - message = String.format("The required %1$s attribute is missing", - attribute); - } else { - message = String.format("The required %1$s attribute *may* be missing", - attribute); - } - } - context.report(ISSUE, element, context.getLocation(element), - message, null); - } - } - } - - private void recordIncludeWidth(String layout, boolean providesWidth) { - if (providesWidth) { - if (mIncludedWidths == null) { - mIncludedWidths = Sets.newHashSet(); - } - mIncludedWidths.add(layout); - } else { - if (mNotIncludedWidths == null) { - mNotIncludedWidths = Sets.newHashSet(); - } - mNotIncludedWidths.add(layout); - } - } - - private void recordIncludeHeight(String layout, boolean providesHeight) { - if (providesHeight) { - if (mIncludedHeights == null) { - mIncludedHeights = Sets.newHashSet(); - } - mIncludedHeights.add(layout); - } else { - if (mNotIncludedHeights == null) { - mNotIncludedHeights = Sets.newHashSet(); - } - mNotIncludedHeights.add(layout); - } - } - - // ---- Implements JavaScanner ---- - - @Override - @Nullable - public List<String> getApplicableMethodNames() { - return Collections.singletonList("inflate"); //$NON-NLS-1$ - } - - @Override - public void visitMethod( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull MethodInvocation call) { - // Handle - // View#inflate(Context context, int resource, ViewGroup root) - // LayoutInflater#inflate(int resource, ViewGroup root) - // LayoutInflater#inflate(int resource, ViewGroup root, boolean attachToRoot) - StrictListAccessor<Expression, MethodInvocation> args = call.astArguments(); - - String layout = null; - int index = 0; - for (Iterator<Expression> iterator = args.iterator(); iterator.hasNext(); index++) { - Expression expression = iterator.next(); - if (expression instanceof Select) { - Select outer = (Select) expression; - Expression operand = outer.astOperand(); - if (operand instanceof Select) { - Select inner = (Select) operand; - if (inner.astOperand() instanceof VariableReference) { - VariableReference reference = (VariableReference) inner.astOperand(); - if (FN_RESOURCE_BASE.equals(reference.astIdentifier().astValue()) - // TODO: constant - && "layout".equals(inner.astIdentifier().astValue())) { - layout = LAYOUT_RESOURCE_PREFIX + outer.astIdentifier().astValue(); - break; - } - } - } - } - } - - if (layout == null) { - lombok.ast.Node method = StringFormatDetector.getParentMethod(call); - if (method != null) { - // Must track local types - index = 0; - String name = StringFormatDetector.getResourceArg(method, call, index); - if (name == null) { - index = 1; - name = StringFormatDetector.getResourceArg(method, call, index); - } - if (name != null) { - layout = LAYOUT_RESOURCE_PREFIX + name; - } - } - if (layout == null) { - // Flow analysis didn't succeed - return; - } - } - - // In all the applicable signatures, the view root argument is immediately after - // the layout resource id. - int viewRootPos = index + 1; - if (viewRootPos < args.size()) { - int i = 0; - Iterator<Expression> iterator = args.iterator(); - while (iterator.hasNext() && i < viewRootPos) { - iterator.next(); - i++; - } - if (iterator.hasNext()) { - Expression viewRoot = iterator.next(); - if (viewRoot instanceof NullLiteral) { - // Yep, this one inflates the given view with a null parent: - // Tag it as such. For now just use the include data structure since - // it has the same net effect - recordIncludeWidth(layout, true); - recordIncludeHeight(layout, true); - } - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ScrollViewChildDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ScrollViewChildDetector.java deleted file mode 100644 index 81bb522..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ScrollViewChildDetector.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.HORIZONTAL_SCROLL_VIEW; -import static com.android.SdkConstants.SCROLL_VIEW; -import static com.android.SdkConstants.VALUE_FILL_PARENT; -import static com.android.SdkConstants.VALUE_MATCH_PARENT; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -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.Attr; -import org.w3c.dom.Element; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * Check which looks at the children of ScrollViews and ensures that they fill/match - * the parent width instead of setting wrap_content. - */ -public class ScrollViewChildDetector extends LayoutDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "ScrollViewSize", //$NON-NLS-1$ - "Checks that ScrollViews use wrap_content in scrolling dimension", - // TODO add a better explanation here! - "ScrollView children must set their `layout_width` or `layout_height` attributes " + - "to `wrap_content` rather than `fill_parent` or `match_parent` in the scrolling " + - "dimension", - Category.CORRECTNESS, - 7, - Severity.WARNING, - ScrollViewChildDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new {@link ScrollViewChildDetector} */ - public ScrollViewChildDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - SCROLL_VIEW, - HORIZONTAL_SCROLL_VIEW - ); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - List<Element> children = LintUtils.getChildren(element); - boolean isHorizontal = HORIZONTAL_SCROLL_VIEW.equals(element.getTagName()); - String attributeName = isHorizontal ? ATTR_LAYOUT_WIDTH : ATTR_LAYOUT_HEIGHT; - for (Element child : children) { - Attr sizeNode = child.getAttributeNodeNS(ANDROID_URI, attributeName); - if (sizeNode == null) { - return; - } - String value = sizeNode.getValue(); - if (VALUE_FILL_PARENT.equals(value) || VALUE_MATCH_PARENT.equals(value)) { - String msg = String.format("This %1$s should use android:%2$s=\"wrap_content\"", - child.getTagName(), attributeName); - context.report(ISSUE, sizeNode, context.getLocation(sizeNode), msg, - null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SdCardDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SdCardDetector.java deleted file mode 100644 index c876b96..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SdCardDetector.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -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 java.io.File; -import java.util.Collections; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Node; -import lombok.ast.StringLiteral; - -/** - * Looks for hardcoded references to /sdcard/. - */ -public class SdCardDetector extends Detector implements Detector.JavaScanner { - /** Hardcoded /sdcard/ references */ - public static final Issue ISSUE = Issue.create( - "SdCardPath", //$NON-NLS-1$ - "Looks for hardcoded references to /sdcard", - - "Your code should not reference the `/sdcard` path directly; instead use " + - "`Environment.getExternalStorageDirectory().getPath()`.\n" + - "\n" + - "Similarly, do not reference the `/data/data/` path directly; it can vary " + - "in multi-user scenarios. Instead, use " + - "`Context.getFilesDir().getPath()`.", - - Category.CORRECTNESS, - 6, - Severity.WARNING, - SdCardDetector.class, - Scope.JAVA_FILE_SCOPE).setMoreInfo( - "http://developer.android.com/guide/topics/data/data-storage.html#filesExternal"); //$NON-NLS-1$ - - /** Constructs a new {@link SdCardDetector} check */ - public SdCardDetector() { - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements JavaScanner ---- - - @Override - public List<Class<? extends Node>> getApplicableNodeTypes() { - return Collections.<Class<? extends Node>>singletonList(StringLiteral.class); - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - return new StringChecker(context); - } - - private static class StringChecker extends ForwardingAstVisitor { - private final JavaContext mContext; - - public StringChecker(JavaContext context) { - mContext = context; - } - - @Override - public boolean visitStringLiteral(StringLiteral node) { - String s = node.astValue(); - if (s.isEmpty()) { - return false; - } - char c = s.charAt(0); - if (c != '/' && c != 'f') { - return false; - } - - if (s.startsWith("/sdcard") //$NON-NLS-1$ - || s.startsWith("/mnt/sdcard/") //$NON-NLS-1$ - || s.startsWith("/system/media/sdcard") //$NON-NLS-1$ - || s.startsWith("file://sdcard/") //$NON-NLS-1$ - || s.startsWith("file:///sdcard/")) { //$NON-NLS-1$ - String message = "Do not hardcode \"/sdcard/\"; " + - "use Environment.getExternalStorageDirectory().getPath() instead"; - Location location = mContext.getLocation(node); - mContext.report(ISSUE, node, location, message, s); - } else if (s.startsWith("/data/data/") //$NON-NLS-1$ - || s.startsWith("/data/user/")) { //$NON-NLS-1$ - String message = "Do not hardcode \"/data/\"; " + - "use Context.getFilesDir().getPath() instead"; - Location location = mContext.getLocation(node); - mContext.report(ISSUE, node, location, message, s); - } - - return false; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java deleted file mode 100644 index f1d349b..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Scope; -import com.android.tools.lint.detector.api.Severity; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.analysis.Analyzer; -import org.objectweb.asm.tree.analysis.AnalyzerException; -import org.objectweb.asm.tree.analysis.BasicInterpreter; -import org.objectweb.asm.tree.analysis.BasicValue; -import org.objectweb.asm.tree.analysis.Frame; - -import java.util.Collections; -import java.util.List; - -/** - * Checks for hardcoded seeds with random numbers. - */ -public class SecureRandomDetector extends Detector implements ClassScanner { - /** Unregistered activities and services */ - public static final Issue ISSUE = Issue.create( - "SecureRandom", //$NON-NLS-1$ - "Looks for suspicious usage of the SecureRandom class", - - "Specifying a fixed seed will cause the instance to return a predictable sequence " + - "of numbers. This may be useful for testing but it is not appropriate for secure use.", - - Category.PERFORMANCE, - 9, - Severity.WARNING, - SecureRandomDetector.class, - Scope.CLASS_FILE_SCOPE). - setMoreInfo("http://developer.android.com/reference/java/security/SecureRandom.html"); - - private static final String SET_SEED = "setSeed"; //$NON-NLS-1$ - private static final String OWNER_SECURE_RANDOM = "java/security/SecureRandom"; //$NON-NLS-1$ - private static final String OWNER_RANDOM = "java/util/Random"; //$NON-NLS-1$ - private static final String VM_SECURE_RANDOM = 'L' + OWNER_SECURE_RANDOM + ';'; - /** Method description for a method that takes a long argument (no return type specified */ - private static final String LONG_ARG = "(J)"; //$NON-NLS-1$ - - /** Constructs a new {@link SecureRandomDetector} */ - public SecureRandomDetector() { - } - - // ---- Implements ClassScanner ---- - - @Override - @Nullable - public List<String> getApplicableCallNames() { - return Collections.singletonList(SET_SEED); - } - - @Override - public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode call) { - String owner = call.owner; - String desc = call.desc; - if (owner.equals(OWNER_SECURE_RANDOM)) { - if (desc.startsWith(LONG_ARG)) { - checkValidSetSeed(context, call); - } else if (desc.startsWith("([B)")) { //$NON-NLS-1$ - // setSeed(byte[]) ... - // We could do some flow analysis here to see whether the byte array getting - // passed in appears to be fixed. - // However, people calling this constructor rather than the simpler one - // with a fixed integer are probably less likely to make that mistake... right? - } - } else if (owner.equals(OWNER_RANDOM) && desc.startsWith(LONG_ARG)) { - // Called setSeed(long) on an instanceof a Random object. Flag this if the instance - // is likely a SecureRandom. - - // Track allocations such that we know whether the type of the call - // is on a SecureRandom rather than a Random - Analyzer analyzer = new Analyzer(new BasicInterpreter() { - @Override - public BasicValue newValue(Type type) { - if (type != null && type.getDescriptor().equals(VM_SECURE_RANDOM)) { - return new BasicValue(type); - } - return super.newValue(type); - } - }); - try { - Frame[] frames = analyzer.analyze(classNode.name, method); - InsnList instructions = method.instructions; - Frame frame = frames[instructions.indexOf(call)]; - int stackSlot = frame.getStackSize(); - for (Type type : Type.getArgumentTypes(desc)) { - stackSlot -= type.getSize(); - } - BasicValue stackValue = (BasicValue) frame.getStack(stackSlot); - Type type = stackValue.getType(); - if (type != null && type.getDescriptor().equals(VM_SECURE_RANDOM)) { - checkValidSetSeed(context, call); - } - } catch (AnalyzerException e) { - context.log(e, null); - } - } else if (owner.equals(OWNER_RANDOM) && desc.startsWith(LONG_ARG)) { - // Called setSeed(long) on an instanceof a Random object. Flag this if the instance - // is likely a SecureRandom. - // TODO - } - } - - private static void checkValidSetSeed(ClassContext context, MethodInsnNode call) { - assert call.name.equals(SET_SEED); - - // Make sure the argument passed is not a literal - AbstractInsnNode prev = LintUtils.getPrevInstruction(call); - if (prev == null) { - return; - } - int opcode = prev.getOpcode(); - if (opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 || opcode == Opcodes.LDC) { - context.report(ISSUE, context.getLocation(call), - "Do not call setSeed() on a SecureRandom with a fixed seed: " + - "it is not secure. Use getSeed().", - null); - } else if (opcode == Opcodes.INVOKESTATIC) { - String methodName = ((MethodInsnNode) prev).name; - if (methodName.equals("currentTimeMillis") || methodName.equals("nanoTime")) { - context.report(ISSUE, context.getLocation(call), - "It is dangerous to seed SecureRandom with the current time because " + - "that value is more predictable to an attacker than the default seed.", - null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java deleted file mode 100644 index b6cc957..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_EXPORTED; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_PATH; -import static com.android.SdkConstants.ATTR_PATH_PATTERN; -import static com.android.SdkConstants.ATTR_PATH_PREFIX; -import static com.android.SdkConstants.ATTR_PERMISSION; -import static com.android.SdkConstants.ATTR_READ_PERMISSION; -import static com.android.SdkConstants.ATTR_WRITE_PERMISSION; -import static com.android.SdkConstants.TAG_ACTIVITY; -import static com.android.SdkConstants.TAG_APPLICATION; -import static com.android.SdkConstants.TAG_GRANT_PERMISSION; -import static com.android.SdkConstants.TAG_INTENT_FILTER; -import static com.android.SdkConstants.TAG_PATH_PERMISSION; -import static com.android.SdkConstants.TAG_PROVIDER; -import static com.android.SdkConstants.TAG_RECEIVER; -import static com.android.SdkConstants.TAG_SERVICE; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LintUtils; -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.android.tools.lint.detector.api.XmlContext; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Identifier; -import lombok.ast.MethodInvocation; -import lombok.ast.StrictListAccessor; - -/** - * Checks that exported services request a permission. - */ -public class SecurityDetector extends Detector implements Detector.XmlScanner, - Detector.JavaScanner { - - /** Exported services */ - public static final Issue EXPORTED_SERVICE = Issue.create( - "ExportedService", //$NON-NLS-1$ - "Checks for exported services that do not require permissions", - "Exported services (services which either set `exported=true` or contain " + - "an intent-filter and do not specify `exported=false`) should define a " + - "permission that an entity must have in order to launch the service " + - "or bind to it. Without this, any application can use this service.", - Category.SECURITY, - 5, - Severity.WARNING, - SecurityDetector.class, - Scope.MANIFEST_SCOPE); - - /** Exported content providers */ - public static final Issue EXPORTED_PROVIDER = Issue.create( - "ExportedContentProvider", //$NON-NLS-1$ - "Checks for exported content providers that do not require permissions", - "Content providers are exported by default and any application on the " + - "system can potentially use them to read and write data. If the content " + - "provider provides access to sensitive data, it should be protected by " + - "specifying `export=false` in the manifest or by protecting it with a " + - "permission that can be granted to other applications.", - Category.SECURITY, - 5, - Severity.WARNING, - SecurityDetector.class, - Scope.MANIFEST_SCOPE); - - /** Exported receivers */ - public static final Issue EXPORTED_RECEIVER = Issue.create( - "ExportedReceiver", //$NON-NLS-1$ - "Checks for exported receivers that do not require permissions", - "Exported receivers (receivers which either set `exported=true` or contain " + - "an intent-filter and do not specify `exported=false`) should define a " + - "permission that an entity must have in order to launch the receiver " + - "or bind to it. Without this, any application can use this receiver.", - Category.SECURITY, - 5, - Severity.WARNING, - SecurityDetector.class, - Scope.MANIFEST_SCOPE); - - /** Content provides which grant all URIs access */ - public static final Issue OPEN_PROVIDER = Issue.create( - "GrantAllUris", //$NON-NLS-1$ - "Checks for <grant-uri-permission> elements where everything is shared", - "The `<grant-uri-permission>` element allows specific paths to be shared. " + - "This detector checks for a path URL of just '/' (everything), which is " + - "probably not what you want; you should limit access to a subset.", - Category.SECURITY, - 7, - Severity.WARNING, - SecurityDetector.class, - Scope.MANIFEST_SCOPE); - - /** Using the world-writable flag */ - public static final Issue WORLD_WRITEABLE = Issue.create( - "WorldWriteableFiles", //$NON-NLS-1$ - "Checks for openFileOutput() and getSharedPreferences() calls passing " + - "MODE_WORLD_WRITEABLE", - "There are cases where it is appropriate for an application to write " + - "world writeable files, but these should be reviewed carefully to " + - "ensure that they contain no private data, and that if the file is " + - "modified by a malicious application it does not trick or compromise " + - "your application.", - Category.SECURITY, - 4, - Severity.WARNING, - SecurityDetector.class, - Scope.JAVA_FILE_SCOPE); - - - /** Using the world-readable flag */ - public static final Issue WORLD_READABLE = Issue.create( - "WorldReadableFiles", //$NON-NLS-1$ - "Checks for openFileOutput() and getSharedPreferences() calls passing " + - "MODE_WORLD_READABLE", - "There are cases where it is appropriate for an application to write " + - "world readable files, but these should be reviewed carefully to " + - "ensure that they contain no private data that is leaked to other " + - "applications.", - Category.SECURITY, - 4, - Severity.WARNING, - SecurityDetector.class, - Scope.JAVA_FILE_SCOPE); - - /** Constructs a new {@link SecurityDetector} check */ - public SecurityDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return file.getName().equals(ANDROID_MANIFEST_XML); - } - - // ---- Implements Detector.XmlScanner ---- - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - TAG_SERVICE, - TAG_GRANT_PERMISSION, - TAG_PROVIDER, - TAG_ACTIVITY, - TAG_RECEIVER - ); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String tag = element.getTagName(); - if (tag.equals(TAG_SERVICE)) { - checkService(context, element); - } else if (tag.equals(TAG_GRANT_PERMISSION)) { - checkGrantPermission(context, element); - } else if (tag.equals(TAG_PROVIDER)) { - checkProvider(context, element); - } else if (tag.equals(TAG_RECEIVER)) { - checkReceiver(context, element); - } - } - - private static boolean getExported(Element element) { - // Used to check whether an activity, service or broadcast receiver is exported. - String exportValue = element.getAttributeNS(ANDROID_URI, ATTR_EXPORTED); - if (exportValue != null && !exportValue.isEmpty()) { - return Boolean.valueOf(exportValue); - } else { - for (Element child : LintUtils.getChildren(element)) { - if (child.getTagName().equals(TAG_INTENT_FILTER)) { - return true; - } - } - } - - return false; - } - - private static boolean isUnprotectedByPermission(Element element) { - // Used to check whether an activity, service or broadcast receiver are - // protected by a permission. - String permission = element.getAttributeNS(ANDROID_URI, ATTR_PERMISSION); - if (permission == null || permission.isEmpty()) { - Node parent = element.getParentNode(); - if (parent.getNodeType() == Node.ELEMENT_NODE - && parent.getNodeName().equals(TAG_APPLICATION)) { - Element application = (Element) parent; - permission = application.getAttributeNS(ANDROID_URI, ATTR_PERMISSION); - return permission == null || permission.isEmpty(); - } - } - - return false; - } - - private static boolean isLauncher(Element element) { - // Checks whether an element is a launcher activity. - for (Element child : LintUtils.getChildren(element)) { - if (child.getTagName().equals(TAG_INTENT_FILTER)) { - for (Element innerChild: LintUtils.getChildren(child)) { - if (innerChild.getTagName().equals("category")) { //$NON-NLS-1$ - String categoryString = innerChild.getAttributeNS(ANDROID_URI, ATTR_NAME); - return "android.intent.category.LAUNCHER".equals(categoryString); //$NON-NLS-1$ - } - } - } - } - - return false; - } - - private static boolean isStandardReceiver(Element element) { - // Checks whether a broadcast receiver receives a standard Android action - for (Element child : LintUtils.getChildren(element)) { - if (child.getTagName().equals(TAG_INTENT_FILTER)) { - for (Element innerChild : LintUtils.getChildren(child)) { - if (innerChild.getTagName().equals("action")) { //$NON-NLS-1$ - String categoryString = innerChild.getAttributeNS(ANDROID_URI, ATTR_NAME); - return categoryString.startsWith("android."); //$NON-NLS-1$ - } - } - } - } - return false; - } - - private static void checkReceiver(XmlContext context, Element element) { - if (getExported(element) && isUnprotectedByPermission(element) && - !isStandardReceiver(element)) { - // No declared permission for this exported receiver: complain - context.report(EXPORTED_RECEIVER, element, context.getLocation(element), - "Exported receiver does not require permission", null); - } - } - - private static void checkService(XmlContext context, Element element) { - if (getExported(element) && isUnprotectedByPermission(element)) { - // No declared permission for this exported service: complain - context.report(EXPORTED_SERVICE, element, context.getLocation(element), - "Exported service does not require permission", null); - } - } - - private static void checkGrantPermission(XmlContext context, Element element) { - Attr path = element.getAttributeNodeNS(ANDROID_URI, ATTR_PATH); - Attr prefix = element.getAttributeNodeNS(ANDROID_URI, ATTR_PATH_PREFIX); - Attr pattern = element.getAttributeNodeNS(ANDROID_URI, ATTR_PATH_PATTERN); - - String msg = "Content provider shares everything; this is potentially dangerous."; - if (path != null && path.getValue().equals("/")) { //$NON-NLS-1$ - context.report(OPEN_PROVIDER, path, context.getLocation(path), msg, null); - } - if (prefix != null && prefix.getValue().equals("/")) { //$NON-NLS-1$ - context.report(OPEN_PROVIDER, prefix, context.getLocation(prefix), msg, null); - } - if (pattern != null && (pattern.getValue().equals("/") //$NON-NLS-1$ - /* || pattern.getValue().equals(".*")*/)) { - context.report(OPEN_PROVIDER, pattern, context.getLocation(pattern), msg, null); - } - } - - private static void checkProvider(XmlContext context, Element element) { - String exportValue = element.getAttributeNS(ANDROID_URI, ATTR_EXPORTED); - // Content providers are exported by default - boolean exported = true; - if (exportValue != null && !exportValue.isEmpty()) { - exported = Boolean.valueOf(exportValue); - } - - if (exported) { - // Just check for some use of permissions. Other Lint checks can check the saneness - // of the permissions. We'll accept the permission, readPermission, or writePermission - // attributes on the provider element, or a path-permission element. - String permission = element.getAttributeNS(ANDROID_URI, ATTR_READ_PERMISSION); - if (permission == null || permission.isEmpty()) { - permission = element.getAttributeNS(ANDROID_URI, ATTR_WRITE_PERMISSION); - if (permission == null || permission.isEmpty()) { - permission = element.getAttributeNS(ANDROID_URI, ATTR_PERMISSION); - if (permission == null || permission.isEmpty()) { - // No permission attributes? Check for path-permission. - - // TODO: Add a Lint check to ensure the path-permission is good, similar to - // the grant-uri-permission check. - boolean hasPermission = false; - for (Element child : LintUtils.getChildren(element)) { - String tag = child.getTagName(); - if (tag.equals(TAG_PATH_PERMISSION)) { - hasPermission = true; - break; - } - } - - if (!hasPermission) { - context.report(EXPORTED_PROVIDER, element, - context.getLocation(element), - "Exported content providers can provide access to " + - "potentially sensitive data", - null); - } - } - } - } - } - } - - // ---- Implements Detector.JavaScanner ---- - - @Override - public List<String> getApplicableMethodNames() { - // These are the API calls that can accept a MODE_WORLD_READABLE/MODE_WORLD_WRITABLE - // argument. - List<String> values = new ArrayList<String>(2); - values.add("openFileOutput"); //$NON-NLS-1$ - values.add("getSharedPreferences"); //$NON-NLS-1$ - return values; - } - - @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - StrictListAccessor<Expression,MethodInvocation> args = node.astArguments(); - Iterator<Expression> iterator = args.iterator(); - while (iterator.hasNext()) { - iterator.next().accept(visitor); - } - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - return new IdentifierVisitor(context); - } - - private static class IdentifierVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; - - public IdentifierVisitor(JavaContext context) { - super(); - mContext = context; - } - - @Override - public boolean visitIdentifier(Identifier node) { - if ("MODE_WORLD_WRITEABLE".equals(node.getDescription())) { //$NON-NLS-1$ - Location location = mContext.getLocation(node); - mContext.report(WORLD_WRITEABLE, node, location, - "Using MODE_WORLD_WRITEABLE when creating files can be " + - "risky, review carefully", - null); - } else if ("MODE_WORLD_READABLE".equals(node.getDescription())) { //$NON-NLS-1$ - Location location = mContext.getLocation(node); - mContext.report(WORLD_READABLE, node, location, - "Using MODE_WORLD_READABLE when creating files can be " + - "risky, review carefully", - null); - } - - return false; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetector.java deleted file mode 100644 index 4b4ba01..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetector.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.Scope; -import com.android.tools.lint.detector.api.Severity; - -import java.util.Collections; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.MethodInvocation; - -/** - * Looks for invocations of android.webkit.WebSettings.setJavaScriptEnabled. - */ -public class SetJavaScriptEnabledDetector extends Detector implements Detector.JavaScanner { - /** Invocations of setJavaScriptEnabled */ - public static final Issue ISSUE = Issue.create("SetJavaScriptEnabled", //$NON-NLS-1$ - "Looks for invocations of android.webkit.WebSettings.setJavaScriptEnabled", - - "Your code should not invoke `setJavaScriptEnabled` if you are not sure that " + - "your app really requires JavaScript support.", - - Category.SECURITY, - 6, - Severity.WARNING, - SetJavaScriptEnabledDetector.class, - Scope.JAVA_FILE_SCOPE).setMoreInfo( - "http://developer.android.com/guide/practices/security.html"); //$NON-NLS-1$ - - /** Constructs a new {@link SetJavaScriptEnabledDetector} check */ - public SetJavaScriptEnabledDetector() { - } - - // ---- Implements JavaScanner ---- - - @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - if (node.astArguments().size() == 1 - && !node.astArguments().first().toString().equals("false")) { //$NON-NLS-1$ - context.report(ISSUE, node, context.getLocation(node), - "Using setJavaScriptEnabled can introduce XSS vulnerabilities " + - "into you application, review carefully.", - null); - } - } - - @Override - public List<String> getApplicableMethodNames() { - return Collections.singletonList("setJavaScriptEnabled"); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SharedPrefsDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SharedPrefsDetector.java deleted file mode 100644 index 76887a5..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SharedPrefsDetector.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Scope; -import com.android.tools.lint.detector.api.Severity; - -import java.io.File; -import java.util.Collections; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.ConstructorDeclaration; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.NormalTypeBody; -import lombok.ast.Return; -import lombok.ast.VariableDeclaration; -import lombok.ast.VariableDefinition; -import lombok.ast.VariableReference; - -/** - * Detector looking for SharedPreferences.edit() calls without a corresponding - * commit() or apply() call - */ -public class SharedPrefsDetector extends Detector implements Detector.JavaScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "CommitPrefEdits", //$NON-NLS-1$ - "Looks for code editing a SharedPreference but forgetting to call commit() on it", - - "After calling `edit()` on a `SharedPreference`, you must call `commit()` " + - "or `apply()` on the editor to save the results.", - - Category.CORRECTNESS, - 6, - Severity.WARNING, - SharedPrefsDetector.class, - Scope.JAVA_FILE_SCOPE); - - /** Constructs a new {@link SharedPrefsDetector} check */ - public SharedPrefsDetector() { - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - - // ---- Implements JavaScanner ---- - - @Override - public List<String> getApplicableMethodNames() { - return Collections.singletonList("edit"); //$NON-NLS-1$ - } - - @Nullable - private static NormalTypeBody findSurroundingTypeBody(Node scope) { - while (scope != null) { - Class<? extends Node> type = scope.getClass(); - // The Lombok AST uses a flat hierarchy of node type implementation classes - // so no need to do instanceof stuff here. - if (type == NormalTypeBody.class) { - return (NormalTypeBody) scope; - } - - scope = scope.getParent(); - } - - return null; - } - - - @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - assert node.astName().astValue().equals("edit"); - Expression operand = node.astOperand(); - if (operand == null) { - return; - } - - // Looking for the specific pattern where you assign the edit() result - // to a local variable; this means we won't recognize some other usages - // of the API (e.g. assigning it to a previously declared variable) but - // is needed until we have type attribution in the AST itself. - Node parent = node.getParent(); - VariableDefinition definition = getLhs(parent); - boolean allowCommitBeforeTarget; - if (definition == null) { - if (operand instanceof VariableReference) { - NormalTypeBody body = findSurroundingTypeBody(parent); - if (body == null) { - return; - } - String variableName = ((VariableReference) operand).astIdentifier().astValue(); - String type = getFieldType(body, variableName); - if (type == null || !type.equals("SharedPreferences")) { //$NON-NLS-1$ - return; - } - allowCommitBeforeTarget = true; - } else { - return; - } - } else { - String type = definition.astTypeReference().toString(); - if (!type.endsWith("SharedPreferences.Editor")) { //$NON-NLS-1$ - if (!type.equals("Editor") || //$NON-NLS-1$ - !LintUtils.isImported(context.compilationUnit, - "android.content.SharedPreferences.Editor")) { //$NON-NLS-1$ - return; - } - } - allowCommitBeforeTarget = false; - } - - Node method = JavaContext.findSurroundingMethod(parent); - if (method == null) { - return; - } - - CommitFinder finder = new CommitFinder(node, allowCommitBeforeTarget); - method.accept(finder); - if (!finder.isCommitCalled()) { - context.report(ISSUE, method, context.getLocation(node), - "SharedPreferences.edit() without a corresponding commit() or apply() call", - null); - } - } - - @Nullable - private static String getFieldType(@NonNull NormalTypeBody cls, @NonNull String name) { - List<Node> children = cls.getChildren(); - for (Node child : children) { - if (child.getClass() == VariableDeclaration.class) { - VariableDeclaration declaration = (VariableDeclaration) child; - VariableDefinition definition = declaration.astDefinition(); - return definition.astTypeReference().toString(); - } - } - - return null; - } - - @Nullable - private static VariableDefinition getLhs(@NonNull Node node) { - while (node != null) { - Class<? extends Node> type = node.getClass(); - // The Lombok AST uses a flat hierarchy of node type implementation classes - // so no need to do instanceof stuff here. - if (type == MethodDeclaration.class || type == ConstructorDeclaration.class) { - return null; - } - if (type == VariableDefinition.class) { - return (VariableDefinition) node; - } - - node = node.getParent(); - } - - return null; - } - - private static class CommitFinder extends ForwardingAstVisitor { - /** The target edit call */ - private final MethodInvocation mTarget; - /** whether it allows the commit call to be seen before the target node */ - private final boolean mAllowCommitBeforeTarget; - /** Whether we've found one of the commit/cancel methods */ - private boolean mFound; - /** Whether we've seen the target edit node yet */ - private boolean mSeenTarget; - - private CommitFinder(MethodInvocation target, boolean allowCommitBeforeTarget) { - mTarget = target; - mAllowCommitBeforeTarget = allowCommitBeforeTarget; - } - - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (node == mTarget) { - mSeenTarget = true; - } else if (mAllowCommitBeforeTarget || mSeenTarget || node.astOperand() == mTarget) { - String name = node.astName().astValue(); - if ("commit".equals(name) || "apply".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$ - // TODO: Do more flow analysis to see whether we're really calling commit/apply - // on the right type of object? - mFound = true; - } - } - - return super.visitMethodInvocation(node); - } - - @Override - public boolean visitReturn(Return node) { - if (node.astValue() == mTarget) { - // If you just do "return editor.commit() don't warn - mFound = true; - } - return super.visitReturn(node); - } - - boolean isCommitCalled() { - return mFound; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/StateListDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/StateListDetector.java deleted file mode 100644 index c0dea30..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/StateListDetector.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Checks for unreachable states in an Android state list definition - */ -public class StateListDetector extends ResourceXmlDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "StateListReachable", //$NON-NLS-1$ - "Looks for unreachable states in a <selector>", - "In a selector, only the last child in the state list should omit a " + - "state qualifier. If not, all subsequent items in the list will be ignored " + - "since the given item will match all.", - Category.CORRECTNESS, - 5, - Severity.WARNING, - StateListDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - private static final String STATE_PREFIX = "state_"; //$NON-NLS-1$ - - /** Constructs a new {@link StateListDetector} */ - public StateListDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.DRAWABLE; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public void visitDocument(@NonNull XmlContext context, @NonNull Document document) { - // TODO: Look for views that don't specify - // Display the error token somewhere so it can be suppressed - // Emit warning at the end "run with --help to learn how to suppress types of errors/checks"; - // ("...and this message.") - - Element root = document.getDocumentElement(); - if (root != null && root.getTagName().equals("selector")) { //$NON-NLS-1$ - List<Element> children = LintUtils.getChildren(root); - Map<Element, Set<String>> states = - new HashMap<Element, Set<String>>(children.size()); - - for (int i = 0; i < children.size(); i++) { - Element child = children.get(i); - NamedNodeMap attributes = child.getAttributes(); - Set<String> stateNames = new HashSet<String>(attributes.getLength()); - states.put(child, stateNames); - - for (int j = 0; j < attributes.getLength(); j++) { - Attr attribute = (Attr) attributes.item(j); - String name = attribute.getLocalName(); - if (name == null) { - continue; - } - if (name.startsWith(STATE_PREFIX)) { - stateNames.add(name + '=' + attribute.getValue()); - } else { - String namespaceUri = attribute.getNamespaceURI(); - if (namespaceUri != null && !namespaceUri.isEmpty() && - !ANDROID_URI.equals(namespaceUri)) { - // There is a custom attribute on this item. - // This could be a state, see - // http://code.google.com/p/android/issues/detail?id=22339 - // so don't flag this one. - stateNames.add(attribute.getName() + '=' + attribute.getValue()); - } - } - } - } - - // See if for each state, any subsequent state fully contains all the same - // state requirements - - for (int i = 0; i < children.size() - 1; i++) { - Element prev = children.get(i); - Set<String> prevStates = states.get(prev); - assert prevStates != null : prev; - for (int j = i + 1; j < children.size(); j++) { - Element current = children.get(j); - Set<String> currentStates = states.get(current); - assert currentStates != null : current; - if (currentStates.containsAll(prevStates)) { - Location location = context.getLocation(current); - Location secondary = context.getLocation(prev); - secondary.setMessage("Earlier item which masks item"); - location.setSecondary(secondary); - context.report(ISSUE, current, location, String.format( - "This item is unreachable because a previous item (item #%1$d) is a more general match than this one", - i + 1), null); - // Don't keep reporting errors for all the remaining cases in this file - return; - } - } - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java deleted file mode 100644 index 033996e..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java +++ /dev/null @@ -1,1299 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.DOT_JAVA; -import static com.android.SdkConstants.FORMAT_METHOD; -import static com.android.SdkConstants.GET_STRING_METHOD; -import static com.android.SdkConstants.TAG_STRING; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.annotations.VisibleForTesting; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.client.api.IJavaParser; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Location.Handle; -import com.android.tools.lint.detector.api.Position; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 com.android.utils.Pair; - -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import lombok.ast.AstVisitor; -import lombok.ast.CharLiteral; -import lombok.ast.ConstructorDeclaration; -import lombok.ast.ConstructorInvocation; -import lombok.ast.Expression; -import lombok.ast.FloatingPointLiteral; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.IntegralLiteral; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.NullLiteral; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.StringLiteral; -import lombok.ast.VariableDefinitionEntry; -import lombok.ast.VariableReference; - -/** - * Check which looks for problems with formatting strings such as inconsistencies between - * translations or between string declaration and string usage in Java. - * <p> - * TODO: Handle Resources.getQuantityString as well - */ -public class StringFormatDetector extends ResourceXmlDetector implements Detector.JavaScanner { - /** Whether formatting strings are invalid */ - public static final Issue INVALID = Issue.create( - "StringFormatInvalid", //$NON-NLS-1$ - "Checks that format strings are valid", - - "If a string contains a '%' character, then the string may be a formatting string " + - "which will be passed to `String.format` from Java code to replace each '%' " + - "occurrence with specific values.\n" + - "\n" + - "This lint warning checks for two related problems:\n" + - "(1) Formatting strings that are invalid, meaning that `String.format` will throw " + - "exceptions at runtime when attempting to use the format string.\n" + - "(2) Strings containing '%' that are not formatting strings getting passed to " + - "a `String.format` call. In this case the '%' will need to be escaped as '%%'.\n" + - "\n" + - "NOTE: Not all Strings which look like formatting strings are intended for " + - "use by `String.format`; for example, they may contain date formats intended " + - "for `android.text.format.Time#format()`. Lint cannot always figure out that " + - "a String is a date format, so you may get false warnings in those scenarios. " + - "See the suppress help topic for information on how to suppress errors in " + - "that case.", - - Category.MESSAGES, - 9, - Severity.ERROR, - StringFormatDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Whether formatting argument types are consistent across translations */ - public static final Issue ARG_COUNT = Issue.create( - "StringFormatCount", //$NON-NLS-1$ - "Ensures that all format strings are used and that the same number is defined " - + "across translations", - - "When a formatted string takes arguments, it usually needs to reference the " + - "same arguments in all translations. There are cases where this is not the case, " + - "so this issue is a warning rather than an error by default. However, this usually " + - "happens when a language is not translated or updated correctly.", - Category.MESSAGES, - 5, - Severity.WARNING, - StringFormatDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Whether the string format supplied in a call to String.format matches the format string */ - public static final Issue ARG_TYPES = Issue.create( - "StringFormatMatches", //$NON-NLS-1$ - "Ensures that the format used in <string> definitions is compatible with the " - + "String.format call", - - "This lint check ensures the following:\n" + - "(1) If there are multiple translations of the format string, then all translations " + - "use the same type for the same numbered arguments\n" + - "(2) The usage of the format string in Java is consistent with the format string, " + - "meaning that the parameter types passed to String.format matches those in the " + - "format string.", - Category.MESSAGES, - 9, - Severity.ERROR, - StringFormatDetector.class, - EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.JAVA_FILE)); - - /** - * Map from a format string name to a list of declaration file and actual - * formatting string content. We're using a list since a format string can be - * defined multiple times, usually for different translations. - */ - private Map<String, List<Pair<Handle, String>>> mFormatStrings; - - /** - * Map of strings that contain percents that aren't formatting strings; these - * should not be passed to String.format. - */ - private final Map<String, Handle> mNotFormatStrings = new HashMap<String, Handle>(); - - /** - * Set of strings that have an unknown format such as date formatting; we should not - * flag these as invalid when used from a String#format call - */ - private Set<String> mIgnoreStrings; - - /** Constructs a new {@link StringFormatDetector} check */ - public StringFormatDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.VALUES; - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - if (LintUtils.endsWith(file.getName(), DOT_JAVA)) { - return mFormatStrings != null; - } - - return super.appliesTo(context, file); - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList(TAG_STRING); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - NodeList childNodes = element.getChildNodes(); - if (childNodes.getLength() > 0) { - if (childNodes.getLength() == 1) { - Node child = childNodes.item(0); - if (child.getNodeType() == Node.TEXT_NODE) { - checkTextNode(context, element, strip(child.getNodeValue())); - } - } else { - // Concatenate children and build up a plain string. - // This is needed to handle xliff localization documents, - // but this needs more work so ignore compound XML documents as - // string values for now: - //StringBuilder sb = new StringBuilder(); - //addText(sb, element); - //if (sb.length() > 0) { - // checkTextNode(context, element, sb.toString()); - //} - } - } - } - - //private static void addText(StringBuilder sb, Node node) { - // if (node.getNodeType() == Node.TEXT_NODE) { - // sb.append(strip(node.getNodeValue().trim())); - // } else { - // NodeList childNodes = node.getChildNodes(); - // for (int i = 0, n = childNodes.getLength(); i < n; i++) { - // addText(sb, childNodes.item(i)); - // } - // } - //} - - private static String strip(String s) { - if (s.length() < 2) { - return s; - } - char first = s.charAt(0); - char last = s.charAt(s.length() - 1); - if (first == last && (first == '\'' || first == '"')) { - return s.substring(1, s.length() - 1); - } - - return s; - } - - private void checkTextNode(XmlContext context, Element element, String text) { - String name = null; - boolean found = false; - - // Look at the String and see if it's a format string (contains - // positional %'s) - for (int j = 0, m = text.length(); j < m; j++) { - char c = text.charAt(j); - if (c == '\\') { - j++; - } - if (c == '%') { - if (name == null) { - name = element.getAttribute(ATTR_NAME); - } - - // Also make sure this String isn't an unformatted String - String formatted = element.getAttribute("formatted"); //$NON-NLS-1$ - if (!formatted.isEmpty() && !Boolean.parseBoolean(formatted)) { - if (!mNotFormatStrings.containsKey(name)) { - Handle handle = context.parser.createLocationHandle(context, element); - handle.setClientData(element); - mNotFormatStrings.put(name, handle); - } - return; - } - - // See if it's not a format string, e.g. "Battery charge is 100%!". - // If so we want to record this name in a special list such that we can - // make sure you don't attempt to reference this string from a String.format - // call. - Matcher matcher = FORMAT.matcher(text); - if (!matcher.find(j)) { - if (!mNotFormatStrings.containsKey(name)) { - Handle handle = context.parser.createLocationHandle(context, element); - handle.setClientData(element); - mNotFormatStrings.put(name, handle); - } - return; - } - - String conversion = matcher.group(6); - int conversionClass = getConversionClass(conversion.charAt(0)); - if (conversionClass == CONVERSION_CLASS_UNKNOWN || matcher.group(5) != null) { - if (mIgnoreStrings == null) { - mIgnoreStrings = new HashSet<String>(); - } - mIgnoreStrings.add(name); - - // Don't process any other strings here; some of them could - // accidentally look like a string, e.g. "%H" is a hash code conversion - // in String.format (and hour in Time formatting). - return; - } - - found = true; - j++; // Ensure that when we process a "%%" we don't separately check the second % - } - } - - if (found && name != null) { - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - // Record it for analysis when seen in Java code - if (mFormatStrings == null) { - mFormatStrings = new HashMap<String, List<Pair<Handle,String>>>(); - } - - List<Pair<Handle, String>> list = mFormatStrings.get(name); - if (list == null) { - list = new ArrayList<Pair<Handle, String>>(); - mFormatStrings.put(name, list); - } - Handle handle = context.parser.createLocationHandle(context, element); - handle.setClientData(element); - list.add(Pair.of(handle, text)); - } - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (mFormatStrings != null) { - boolean checkCount = context.isEnabled(ARG_COUNT); - boolean checkValid = context.isEnabled(INVALID); - boolean checkTypes = context.isEnabled(ARG_TYPES); - - // Ensure that all the format strings are consistent with respect to each other; - // e.g. they all have the same number of arguments, they all use all the - // arguments, and they all use the same types for all the numbered arguments - for (Map.Entry<String, List<Pair<Handle, String>>> entry : mFormatStrings.entrySet()) { - String name = entry.getKey(); - List<Pair<Handle, String>> list = entry.getValue(); - - // Check argument counts - if (checkCount) { - checkArity(context, name, list); - } - - // Check argument types (and also make sure that the formatting strings are valid) - if (checkValid || checkTypes) { - checkTypes(context, checkValid, checkTypes, name, list); - } - } - } - } - - private static void checkTypes(Context context, boolean checkValid, - boolean checkTypes, String name, List<Pair<Handle, String>> list) { - Map<Integer, String> types = new HashMap<Integer, String>(); - Map<Integer, Handle> typeDefinition = new HashMap<Integer, Handle>(); - for (Pair<Handle, String> pair : list) { - Handle handle = pair.getFirst(); - String formatString = pair.getSecond(); - - //boolean warned = false; - Matcher matcher = FORMAT.matcher(formatString); - int index = 0; - int prevIndex = 0; - int nextNumber = 1; - while (true) { - if (matcher.find(index)) { - int matchStart = matcher.start(); - // Make sure this is not an escaped '%' - for (; prevIndex < matchStart; prevIndex++) { - char c = formatString.charAt(prevIndex); - if (c == '\\') { - prevIndex++; - } - } - if (prevIndex > matchStart) { - // We're in an escape, ignore this result - index = prevIndex; - continue; - } - - index = matcher.end(); // Ensure loop proceeds - String str = formatString.substring(matchStart, matcher.end()); - if (str.equals("%%")) { //$NON-NLS-1$ - // Just an escaped % - continue; - } - - if (checkValid) { - // Make sure it's a valid format string - if (str.length() > 2 && str.charAt(str.length() - 2) == ' ') { - char last = str.charAt(str.length() - 1); - // If you forget to include the conversion character, e.g. - // "Weight=%1$ g" instead of "Weight=%1$d g", then - // you're going to end up with a format string interpreted as - // "%1$ g". This means that the space character is interpreted - // as a flag character, but it can only be a flag character - // when used in conjunction with the numeric conversion - // formats (d, o, x, X). If that's not the case, make a - // dedicated error message - if (last != 'd' && last != 'o' && last != 'x' && last != 'X') { - Object clientData = handle.getClientData(); - if (clientData instanceof Node) { - if (context.getDriver().isSuppressed(INVALID, - (Node) clientData)) { - return; - } - } - - Location location = handle.resolve(); - String message = String.format( - "Incorrect formatting string %1$s; missing conversion " + - "character in '%2$s' ?", name, str); - context.report(INVALID, location, message, null); - //warned = true; - continue; - } - } - } - - if (!checkTypes) { - continue; - } - - // Shouldn't throw a number format exception since we've already - // matched the pattern in the regexp - int number; - String numberString = matcher.group(1); - if (numberString != null) { - // Strip off trailing $ - numberString = numberString.substring(0, numberString.length() - 1); - number = Integer.parseInt(numberString); - nextNumber = number + 1; - } else { - number = nextNumber++; - } - String format = matcher.group(6); - String currentFormat = types.get(number); - if (currentFormat == null) { - types.put(number, format); - typeDefinition.put(number, handle); - } else if (!currentFormat.equals(format) - && isIncompatible(currentFormat.charAt(0), format.charAt(0))) { - - Object clientData = handle.getClientData(); - if (clientData instanceof Node) { - if (context.getDriver().isSuppressed(ARG_TYPES, (Node) clientData)) { - return; - } - } - - Location location = handle.resolve(); - // Attempt to limit the location range to just the formatting - // string in question - location = refineLocation(context, location, formatString, - matcher.start(), matcher.end()); - Location otherLocation = typeDefinition.get(number).resolve(); - otherLocation.setMessage("Conflicting argument type here"); - location.setSecondary(otherLocation); - File f = otherLocation.getFile(); - String message = String.format( - "Inconsistent formatting types for argument #%1$d in " + - "format string %2$s ('%3$s'): Found both '%4$s' and '%5$s' " + - "(in %6$s)", - number, name, - str, - currentFormat, format, - f.getParentFile().getName() + File.separator + f.getName()); - //warned = true; - context.report(ARG_TYPES, location, message, null); - break; - } - } else { - break; - } - } - - // Check that the format string is valid by actually attempting to instantiate - // it. We only do this if we haven't already complained about this string - // for other reasons. - /* Check disabled for now: it had many false reports due to conversion - * errors (which is expected since we just pass in strings), but once those - * are eliminated there aren't really any other valid error messages returned - * (for example, calling the formatter with bogus formatting flags always just - * returns a "conversion" error. It looks like we'd need to actually pass compatible - * arguments to trigger other types of formatting errors such as precision errors. - if (!warned && checkValid) { - try { - formatter.format(formatString, "", "", "", "", "", "", "", - "", "", "", "", "", "", ""); - - } catch (IllegalFormatException t) { // TODO: UnknownFormatConversionException - if (!t.getLocalizedMessage().contains(" != ") - && !t.getLocalizedMessage().contains("Conversion")) { - Location location = handle.resolve(); - context.report(INVALID, location, - String.format("Wrong format for %1$s: %2$s", - name, t.getLocalizedMessage()), null); - } - } - } - */ - } - } - - /** - * Returns true if two String.format conversions are "incompatible" (meaning - * that using these two for the same argument across different translations - * is more likely an error than intentional. Some conversions are - * incompatible, e.g. "d" and "s" where one is a number and string, whereas - * others may work (e.g. float versus integer) but are probably not - * intentional. - */ - private static boolean isIncompatible(char conversion1, char conversion2) { - int class1 = getConversionClass(conversion1); - int class2 = getConversionClass(conversion2); - return class1 != class2 - && class1 != CONVERSION_CLASS_UNKNOWN - && class2 != CONVERSION_CLASS_UNKNOWN; - } - - private static final int CONVERSION_CLASS_UNKNOWN = 0; - private static final int CONVERSION_CLASS_STRING = 1; - private static final int CONVERSION_CLASS_CHARACTER = 2; - private static final int CONVERSION_CLASS_INTEGER = 3; - private static final int CONVERSION_CLASS_FLOAT = 4; - private static final int CONVERSION_CLASS_BOOLEAN = 5; - private static final int CONVERSION_CLASS_HASHCODE = 6; - private static final int CONVERSION_CLASS_PERCENT = 7; - private static final int CONVERSION_CLASS_NEWLINE = 8; - private static final int CONVERSION_CLASS_DATETIME = 9; - - private static int getConversionClass(char conversion) { - // See http://developer.android.com/reference/java/util/Formatter.html - switch (conversion) { - case 't': // Time/date conversion - case 'T': - return CONVERSION_CLASS_DATETIME; - case 's': // string - case 'S': // Uppercase string - return CONVERSION_CLASS_STRING; - case 'c': // character - case 'C': // Uppercase character - return CONVERSION_CLASS_CHARACTER; - case 'd': // decimal - case 'o': // octal - case 'x': // hex - case 'X': - return CONVERSION_CLASS_INTEGER; - case 'f': // decimal float - case 'e': // exponential float - case 'E': - case 'g': // decimal or exponential depending on size - case 'G': - case 'a': // hex float - case 'A': - return CONVERSION_CLASS_FLOAT; - case 'b': // boolean - case 'B': - return CONVERSION_CLASS_BOOLEAN; - case 'h': // boolean - case 'H': - return CONVERSION_CLASS_HASHCODE; - case '%': // literal - return CONVERSION_CLASS_PERCENT; - case 'n': // literal - return CONVERSION_CLASS_NEWLINE; - } - - return CONVERSION_CLASS_UNKNOWN; - } - - private static Location refineLocation(Context context, Location location, - String formatString, int substringStart, int substringEnd) { - Position startLocation = location.getStart(); - Position endLocation = location.getStart(); - if (startLocation != null && endLocation != null) { - int startOffset = startLocation.getOffset(); - int endOffset = endLocation.getOffset(); - if (startOffset >= 0) { - String contents = context.getClient().readFile(location.getFile()); - if (contents != null - && endOffset <= contents.length() && startOffset < endOffset) { - int formatOffset = contents.indexOf(formatString, startOffset); - if (formatOffset != -1 && formatOffset <= endOffset) { - return Location.create(context.file, contents, - formatOffset + substringStart, formatOffset + substringEnd); - } - } - } - } - - return location; - } - - /** - * Check that the number of arguments in the format string is consistent - * across translations, and that all arguments are used - */ - private static void checkArity(Context context, String name, List<Pair<Handle, String>> list) { - // Check to make sure that the argument counts and types are consistent - int prevCount = -1; - for (Pair<Handle, String> pair : list) { - Set<Integer> indices = new HashSet<Integer>(); - int count = getFormatArgumentCount(pair.getSecond(), indices); - Handle handle = pair.getFirst(); - if (prevCount != -1 && prevCount != count) { - Object clientData = handle.getClientData(); - if (clientData instanceof Node) { - if (context.getDriver().isSuppressed(ARG_COUNT, (Node) clientData)) { - return; - } - } - Location location = handle.resolve(); - Location secondary = list.get(0).getFirst().resolve(); - secondary.setMessage("Conflicting number of arguments here"); - location.setSecondary(secondary); - String message = String.format( - "Inconsistent number of arguments in formatting string %1$s; " + - "found both %2$d and %3$d", name, prevCount, count); - context.report(ARG_COUNT, location, message, null); - break; - } - - for (int i = 1; i <= count; i++) { - if (!indices.contains(i)) { - Object clientData = handle.getClientData(); - if (clientData instanceof Node) { - if (context.getDriver().isSuppressed(ARG_COUNT, (Node) clientData)) { - return; - } - } - - Set<Integer> all = new HashSet<Integer>(); - for (int j = 1; j < count; j++) { - all.add(j); - } - all.removeAll(indices); - List<Integer> sorted = new ArrayList<Integer>(all); - Collections.sort(sorted); - Location location = handle.resolve(); - String message = String.format( - "Formatting string '%1$s' is not referencing numbered arguments %2$s", - name, sorted); - context.report(ARG_COUNT, location, message, null); - break; - } - } - - prevCount = count; - } - } - - // See java.util.Formatter docs - private static final Pattern FORMAT = Pattern.compile( - // Generic format: - // %[argument_index$][flags][width][.precision]conversion - // - "%" + //$NON-NLS-1$ - // Argument Index - "(\\d+\\$)?" + //$NON-NLS-1$ - // Flags - "([-+#, 0(\\<]*)?" + //$NON-NLS-1$ - // Width - "(\\d+)?" + //$NON-NLS-1$ - // Precision - "(\\.\\d+)?" + //$NON-NLS-1$ - // Conversion. These are all a single character, except date/time conversions - // which take a prefix of t/T: - "([tT])?" + //$NON-NLS-1$ - // The current set of conversion characters are - // b,h,s,c,d,o,x,e,f,g,a,t (as well as all those as upper-case characters), plus - // n for newlines and % as a literal %. And then there are all the time/date - // characters: HIKLm etc. Just match on all characters here since there should - // be at least one. - "([a-zA-Z%])"); //$NON-NLS-1$ - - /** Given a format string returns the format type of the given argument */ - @VisibleForTesting - static String getFormatArgumentType(String s, int argument) { - Matcher matcher = FORMAT.matcher(s); - int index = 0; - int prevIndex = 0; - int nextNumber = 1; - while (true) { - if (matcher.find(index)) { - int matchStart = matcher.start(); - // Make sure this is not an escaped '%' - for (; prevIndex < matchStart; prevIndex++) { - char c = s.charAt(prevIndex); - if (c == '\\') { - prevIndex++; - } - } - if (prevIndex > matchStart) { - // We're in an escape, ignore this result - index = prevIndex; - continue; - } - - // Shouldn't throw a number format exception since we've already - // matched the pattern in the regexp - int number; - String numberString = matcher.group(1); - if (numberString != null) { - // Strip off trailing $ - numberString = numberString.substring(0, numberString.length() - 1); - number = Integer.parseInt(numberString); - nextNumber = number + 1; - } else { - number = nextNumber++; - } - - if (number == argument) { - return matcher.group(6); - } - index = matcher.end(); - } else { - break; - } - } - - return null; - } - - /** - * Given a format string returns the number of required arguments. If the - * {@code seenArguments} parameter is not null, put the indices of any - * observed arguments into it. - */ - @VisibleForTesting - static int getFormatArgumentCount(String s, Set<Integer> seenArguments) { - Matcher matcher = FORMAT.matcher(s); - int index = 0; - int prevIndex = 0; - int nextNumber = 1; - int max = 0; - while (true) { - if (matcher.find(index)) { - if ("%".equals(matcher.group(6))) { //$NON-NLS-1$ - index = matcher.end(); - continue; - } - int matchStart = matcher.start(); - // Make sure this is not an escaped '%' - for (; prevIndex < matchStart; prevIndex++) { - char c = s.charAt(prevIndex); - if (c == '\\') { - prevIndex++; - } - } - if (prevIndex > matchStart) { - // We're in an escape, ignore this result - index = prevIndex; - continue; - } - - // Shouldn't throw a number format exception since we've already - // matched the pattern in the regexp - int number; - String numberString = matcher.group(1); - if (numberString != null) { - // Strip off trailing $ - numberString = numberString.substring(0, numberString.length() - 1); - number = Integer.parseInt(numberString); - nextNumber = number + 1; - } else { - number = nextNumber++; - } - - if (number > max) { - max = number; - } - if (seenArguments != null) { - seenArguments.add(number); - } - - index = matcher.end(); - } else { - break; - } - } - - return max; - } - - /** - * Determines whether the given {@link String#format(String, Object...)} - * formatting string is "locale dependent", meaning that its output depends - * on the locale. This is the case if it for example references decimal - * numbers of dates and times. - * - * @param format the format string - * @return true if the format is locale sensitive, false otherwise - */ - public static boolean isLocaleSpecific(@NonNull String format) { - if (format.indexOf('%') == -1) { - return false; - } - - String s = format; - Matcher matcher = FORMAT.matcher(s); - int index = 0; - int prevIndex = 0; - while (true) { - if (matcher.find(index)) { - int matchStart = matcher.start(); - // Make sure this is not an escaped '%' - for (; prevIndex < matchStart; prevIndex++) { - char c = s.charAt(prevIndex); - if (c == '\\') { - prevIndex++; - } - } - if (prevIndex > matchStart) { - // We're in an escape, ignore this result - index = prevIndex; - continue; - } - - String type = matcher.group(6); - if (!type.isEmpty()) { - char t = type.charAt(0); - - // The following formatting characters are locale sensitive: - switch (t) { - case 'd': // decimal integer - case 'e': // scientific - case 'E': - case 'f': // decimal float - case 'g': // general - case 'G': - case 't': // date/time - case 'T': - return true; - } - } - index = matcher.end(); - } else { - break; - } - } - - return false; - } - - @Override - public List<String> getApplicableMethodNames() { - return Arrays.asList(FORMAT_METHOD, GET_STRING_METHOD); - } - - @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - if (mFormatStrings == null) { - return; - } - - String methodName = node.astName().getDescription(); - if (methodName.equals(FORMAT_METHOD)) { - // String.format(getResources().getString(R.string.foo), arg1, arg2, ...) - // Check that the arguments in R.string.foo match arg1, arg2, ... - if (node.astOperand() instanceof VariableReference) { - VariableReference ref = (VariableReference) node.astOperand(); - if ("String".equals(ref.astIdentifier().astValue())) { //$NON-NLS-1$ - // Found a String.format call - // Look inside to see if we can find an R string - // Find surrounding method - checkFormatCall(context, node); - } - } - } else { - // getResources().getString(R.string.foo, arg1, arg2, ...) - // Check that the arguments in R.string.foo match arg1, arg2, ... - if (node.astArguments().size() > 1 && node.astOperand() != null ) { - checkFormatCall(context, node); - } - } - } - - private void checkFormatCall(JavaContext context, MethodInvocation node) { - lombok.ast.Node current = getParentMethod(node); - if (current != null) { - checkStringFormatCall(context, current, node); - } - } - - /** - * Check the given String.format call (with the given arguments) to see if - * the string format is being used correctly - * - * @param context the context to report errors to - * @param method the method containing the {@link String#format} call - * @param call the AST node for the {@link String#format} - */ - private void checkStringFormatCall( - JavaContext context, - lombok.ast.Node method, - MethodInvocation call) { - - StrictListAccessor<Expression, MethodInvocation> args = call.astArguments(); - if (args.isEmpty()) { - return; - } - - StringTracker tracker = new StringTracker(method, call, 0); - method.accept(tracker); - String name = tracker.getFormatStringName(); - if (name == null) { - return; - } - - if (mIgnoreStrings != null && mIgnoreStrings.contains(name)) { - return; - } - - if (mNotFormatStrings.containsKey(name)) { - Handle handle = mNotFormatStrings.get(name); - Object clientData = handle.getClientData(); - if (clientData instanceof Node) { - if (context.getDriver().isSuppressed(INVALID, (Node) clientData)) { - return; - } - } - Location location = handle.resolve(); - String message = String.format( - "Format string '%1$s' is not a valid format string so it should not be " + - "passed to String.format", - name); - context.report(INVALID, call, location, message, null); - return; - } - - // TODO: Need type information in the AST - Iterator<Expression> argIterator = args.iterator(); - Expression first = argIterator.next(); - Expression second = argIterator.hasNext() ? argIterator.next() : null; - String firstName = first.toString(); - boolean specifiesLocale = firstName.startsWith("Locale.") //$NON-NLS-1$ - || firstName.contains("locale") //$NON-NLS-1$ - || firstName.equals("null") //$NON-NLS-1$ - || second != null && second.toString().contains("getString"); //$NON-NLS-1$ - - List<Pair<Handle, String>> list = mFormatStrings.get(name); - if (list != null) { - for (Pair<Handle, String> pair : list) { - String s = pair.getSecond(); - int count = getFormatArgumentCount(s, null); - Handle handle = pair.getFirst(); - if (count != args.size() - 1 - (specifiesLocale ? 1 : 0)) { - Location location = context.parser.getLocation(context, call); - Location secondary = handle.resolve(); - secondary.setMessage(String.format("This definition requires %1$d arguments", - count)); - location.setSecondary(secondary); - String message = String.format( - "Wrong argument count, format string %1$s requires %2$d but format " + - "call supplies %3$d", - name, count, args.size() - 1); - context.report(ARG_TYPES, method, location, message, null); - } else { - for (int i = 1; i <= count; i++) { - int argumentIndex = i + (specifiesLocale ? 1 : 0); - Class<?> type = tracker.getArgumentType(argumentIndex); - if (type != null) { - boolean valid = true; - String formatType = getFormatArgumentType(s, i); - char last = formatType.charAt(formatType.length() - 1); - if (formatType.length() >= 2 && - Character.toLowerCase( - formatType.charAt(formatType.length() - 2)) == 't') { - // Date time conversion. - // TODO - continue; - } - switch (last) { - // Booleans. It's okay to pass objects to these; - // it will print "true" if non-null, but it's - // unusual and probably not intended. - case 'b': - case 'B': - valid = type == Boolean.TYPE; - break; - - // Numeric: integer and floats in various formats - case 'x': - case 'X': - case 'd': - case 'o': - case 'e': - case 'E': - case 'f': - case 'g': - case 'G': - case 'a': - case 'A': - valid = type == Integer.TYPE - || type == Float.TYPE; - break; - case 'c': - case 'C': - // Unicode character - valid = type == Character.TYPE; - break; - case 'h': - case 'H': // Hex print of hash code of objects - case 's': - case 'S': - // String. Can pass anything, but warn about - // numbers since you may have meant more - // specific formatting. Use special issue - // explanation for this? - valid = type != Boolean.TYPE && - !type.isAssignableFrom(Number.class); - break; - } - - if (!valid) { - IJavaParser parser = context.parser; - Expression argument = tracker.getArgument(argumentIndex); - Location location = parser.getLocation(context, argument); - Location secondary = handle.resolve(); - secondary.setMessage("Conflicting argument declaration here"); - location.setSecondary(secondary); - - String message = String.format( - "Wrong argument type for formatting argument '#%1$d' " + - "in %2$s: conversion is '%3$s', received %4$s", - i, name, formatType, type.getSimpleName()); - context.report(ARG_TYPES, method, location, message, null); - } - } - } - } - } - } - } - - /** Returns the parent method of the given AST node */ - @Nullable - static lombok.ast.Node getParentMethod(@NonNull lombok.ast.Node node) { - lombok.ast.Node current = node.getParent(); - while (current != null - && !(current instanceof MethodDeclaration) - && !(current instanceof ConstructorDeclaration)) { - current = current.getParent(); - } - - return current; - } - - /** Returns the resource name corresponding to the first argument in the given call */ - static String getResourceForFirstArg(lombok.ast.Node method, lombok.ast.Node call) { - assert call instanceof MethodInvocation || call instanceof ConstructorInvocation; - StringTracker tracker = new StringTracker(method, call, 0); - method.accept(tracker); - String name = tracker.getFormatStringName(); - - return name; - } - - /** Returns the resource name corresponding to the given argument in the given call */ - static String getResourceArg(lombok.ast.Node method, lombok.ast.Node call, int argIndex) { - assert call instanceof MethodInvocation || call instanceof ConstructorInvocation; - StringTracker tracker = new StringTracker(method, call, argIndex); - method.accept(tracker); - String name = tracker.getFormatStringName(); - - return name; - } - - /** - * Given a variable reference, finds the original R.string value corresponding to it. - * For example: - * <pre> - * {@code - * String target = "World"; - * String hello = getResources().getString(R.string.hello); - * String output = String.format(hello, target); - * } - * </pre> - * - * Given the {@code String.format} call, we want to find out what R.string resource - * corresponds to the first argument, in this case {@code R.string.hello}. - * To do this, we look for R.string references, and track those through assignments - * until we reach the target node. - * <p> - * In addition, it also does some primitive type tracking such that it (in some cases) - * can answer questions about the types of variables. This allows it to check whether - * certain argument types are valid. Note however that it does not do full-blown - * type analysis by checking method call signatures and so on. - */ - private static class StringTracker extends ForwardingAstVisitor { - /** Method we're searching within */ - private final lombok.ast.Node mTop; - /** The argument index in the method we're targeting */ - private final int mArgIndex; - /** Map from variable name to corresponding string resource name */ - private final Map<String, String> mMap = new HashMap<String, String>(); - /** Map from variable name to corresponding type */ - private final Map<String, Class<?>> mTypes = new HashMap<String, Class<?>>(); - /** The AST node for the String.format we're interested in */ - private final lombok.ast.Node mTargetNode; - private boolean mDone; - /** - * Result: the name of the string resource being passed to the - * String.format, if any - */ - private String mName; - - public StringTracker(lombok.ast.Node top, lombok.ast.Node targetNode, int argIndex) { - mTop = top; - mArgIndex = argIndex; - mTargetNode = targetNode; - } - - public String getFormatStringName() { - return mName; - } - - /** Returns the argument type of the given formatting argument of the - * target node. Note: This is in the formatting string, which is one higher - * than the String.format parameter number, since the first argument is the - * formatting string itself. - * - * @param argument the argument number - * @return the class (such as {@link Integer#TYPE} etc) or null if not known - */ - public Class<?> getArgumentType(int argument) { - Expression arg = getArgument(argument); - if (arg != null) { - Class<?> type = getType(arg); - if (type != null) { - return type; - } - } - - return null; - } - - public Expression getArgument(int argument) { - if (!(mTargetNode instanceof MethodInvocation)) { - return null; - } - MethodInvocation call = (MethodInvocation) mTargetNode; - StrictListAccessor<Expression, MethodInvocation> args = call.astArguments(); - if (argument >= args.size()) { - return null; - } - - Iterator<Expression> iterator = args.iterator(); - int index = 0; - while (iterator.hasNext()) { - Expression arg = iterator.next(); - if (index++ == argument) { - return arg; - } - } - - return null; - } - - @Override - public boolean visitNode(lombok.ast.Node node) { - if (mDone) { - return true; - } - - return super.visitNode(node); - } - - @Override - public boolean visitVariableReference(VariableReference node) { - if (node.astIdentifier().getDescription().equals("R") && //$NON-NLS-1$ - node.getParent() instanceof Select && - node.getParent().getParent() instanceof Select) { - - // See if we're on the right hand side of an assignment - lombok.ast.Node current = node.getParent().getParent(); - String reference = ((Select) current).astIdentifier().astValue(); - - while (current != mTop && !(current instanceof VariableDefinitionEntry)) { - if (current == mTargetNode) { - mName = reference; - mDone = true; - return false; - } - current = current.getParent(); - } - if (current instanceof VariableDefinitionEntry) { - VariableDefinitionEntry entry = (VariableDefinitionEntry) current; - String variable = entry.astName().astValue(); - mMap.put(variable, reference); - } - } - - return false; - } - - @Nullable - private Expression getTargetArgument() { - Iterator<Expression> iterator; - if (mTargetNode instanceof MethodInvocation) { - iterator = ((MethodInvocation) mTargetNode).astArguments().iterator(); - } else if (mTargetNode instanceof ConstructorInvocation) { - iterator = ((ConstructorInvocation) mTargetNode).astArguments().iterator(); - } else { - return null; - } - int i = 0; - while (i < mArgIndex && iterator.hasNext()) { - iterator.next(); - i++; - } - if (iterator.hasNext()) { - return iterator.next(); - } - - return null; - } - - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (node == mTargetNode) { - Expression arg = getTargetArgument(); - if (arg instanceof VariableReference) { - VariableReference reference = (VariableReference) arg; - String variable = reference.astIdentifier().astValue(); - mName = mMap.get(variable); - mDone = true; - return true; - } - } - - // Is this a getString() call? On a resource object? If so, - // promote the resource argument up to the left hand side - return super.visitMethodInvocation(node); - } - - @Override - public boolean visitConstructorInvocation(ConstructorInvocation node) { - if (node == mTargetNode) { - Expression arg = getTargetArgument(); - if (arg instanceof VariableReference) { - VariableReference reference = (VariableReference) arg; - String variable = reference.astIdentifier().astValue(); - mName = mMap.get(variable); - mDone = true; - return true; - } - } - - // Is this a getString() call? On a resource object? If so, - // promote the resource argument up to the left hand side - return super.visitConstructorInvocation(node); - } - - @Override - public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) { - String name = node.astName().astValue(); - Expression rhs = node.astInitializer(); - Class<?> type = getType(rhs); - if (type != null) { - mTypes.put(name, type); - } else { - // Make sure we're not visiting the String.format node itself. If you have - // msg = String.format("%1$s", msg) - // then we'd be wiping out the type of "msg" before visiting the - // String.format call! - if (rhs != mTargetNode) { - mTypes.remove(name); - } - } - - return super.visitVariableDefinitionEntry(node); - } - - private Class<?> getType(Expression expression) { - if (expression instanceof VariableReference) { - VariableReference reference = (VariableReference) expression; - String variable = reference.astIdentifier().astValue(); - return mTypes.get(variable); - } else if (expression instanceof MethodInvocation) { - MethodInvocation method = (MethodInvocation) expression; - String methodName = method.astName().astValue(); - if (methodName.equals(GET_STRING_METHOD)) { - return String.class; - } - } else if (expression instanceof StringLiteral) { - return String.class; - } else if (expression instanceof IntegralLiteral) { - return Integer.TYPE; - } else if (expression instanceof FloatingPointLiteral) { - return Float.TYPE; - } else if (expression instanceof CharLiteral) { - return Character.TYPE; - } else if (expression instanceof NullLiteral) { - return Object.class; - } - - return null; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/StyleCycleDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/StyleCycleDetector.java deleted file mode 100644 index 8ce103b..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/StyleCycleDetector.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_PARENT; -import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.TAG_STYLE; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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.Attr; -import org.w3c.dom.Element; - -import java.util.Collection; -import java.util.Collections; - -/** - * Checks for cycles in style definitions - */ -public class StyleCycleDetector extends ResourceXmlDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "StyleCycle", //$NON-NLS-1$ - "Looks for cycles in style definitions", - "There should be no cycles in style definitions as this can lead to runtime " + - "exceptions.", - Category.CORRECTNESS, - 8, - Severity.FATAL, - StyleCycleDetector.class, - Scope.RESOURCE_FILE_SCOPE).setMoreInfo( - "http://developer.android.com/guide/topics/ui/themes.html#Inheritance"); //$NON-NLS-1$ - - /** Constructs a new {@link StyleCycleDetector} */ - public StyleCycleDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.VALUES; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singleton(TAG_STYLE); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - Attr parentNode = element.getAttributeNode(ATTR_PARENT); - if (parentNode != null) { - String parent = parentNode.getValue(); - String name = element.getAttribute(ATTR_NAME); - if (parent.endsWith(name) && - parent.equals(STYLE_RESOURCE_PREFIX + name)) { - context.report(ISSUE, parentNode, context.getLocation(parentNode), - String.format("Style %1$s should not extend itself", name), null); - } else if (parent.startsWith(STYLE_RESOURCE_PREFIX) - && parent.startsWith(name, STYLE_RESOURCE_PREFIX.length()) - && parent.startsWith(".", STYLE_RESOURCE_PREFIX.length() + name.length())) { - context.report(ISSUE, parentNode, context.getLocation(parentNode), - String.format("Potential cycle: %1$s is the implied parent of %2$s and " + - "this defines the opposite", name, - parent.substring(STYLE_RESOURCE_PREFIX.length())), null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SystemPermissionsDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SystemPermissionsDetector.java deleted file mode 100644 index 21fb6c0..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/SystemPermissionsDetector.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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.ANDROID_MANIFEST_XML; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.TAG_USES_PERMISSION; - -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.Detector; -import com.android.tools.lint.detector.api.Issue; -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.Attr; -import org.w3c.dom.Element; - -import java.io.File; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; - -/** - * Checks if an application wants to use permissions that can only be used by - * system applications. - */ -public class SystemPermissionsDetector extends Detector implements Detector.XmlScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "ProtectedPermissions", //$NON-NLS-1$ - "Looks for permissions that are only granted to system apps", - - "Permissions with the protection level signature or signatureOrSystem are only " + - "granted to system apps. If an app is a regular non-system app, it will never be " + - "able to use these permissions.", - - Category.CORRECTNESS, - 5, - Severity.ERROR, - SystemPermissionsDetector.class, - EnumSet.of(Scope.MANIFEST)); - - // List of permissions have the protection levels signature or systemOrSignature. - // This list must be sorted alphabetically. - private static final String[] SYSTEM_PERMISSIONS = new String[] { - "android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE", - "android.permission.ACCESS_CACHE_FILESYSTEM", - "android.permission.ACCESS_CHECKIN_PROPERTIES", - "android.permission.ACCESS_MTP", - "android.permission.ACCESS_SURFACE_FLINGER", - "android.permission.ACCOUNT_MANAGER", - "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK", - "android.permission.ASEC_ACCESS", - "android.permission.ASEC_CREATE", - "android.permission.ASEC_DESTROY", - "android.permission.ASEC_MOUNT_UNMOUNT", - "android.permission.ASEC_RENAME", - "android.permission.BACKUP", - "android.permission.BIND_APPWIDGET", - "android.permission.BIND_DEVICE_ADMIN", - "android.permission.BIND_INPUT_METHOD", - "android.permission.BIND_PACKAGE_VERIFIER", - "android.permission.BIND_REMOTEVIEWS", - "android.permission.BIND_TEXT_SERVICE", - "android.permission.BIND_VPN_SERVICE", - "android.permission.BIND_WALLPAPER", - "android.permission.BRICK", - "android.permission.BROADCAST_PACKAGE_REMOVED", - "android.permission.BROADCAST_SMS", - "android.permission.BROADCAST_WAP_PUSH", - "android.permission.CALL_PRIVILEGED", - "android.permission.CHANGE_BACKGROUND_DATA_SETTING", - "android.permission.CHANGE_COMPONENT_ENABLED_STATE", - "android.permission.CLEAR_APP_USER_DATA", - "android.permission.CONFIRM_FULL_BACKUP", - "android.permission.CONNECTIVITY_INTERNAL", - "android.permission.CONTROL_LOCATION_UPDATES", - "android.permission.COPY_PROTECTED_DATA", - "android.permission.CRYPT_KEEPER", - "android.permission.DELETE_CACHE_FILES", - "android.permission.DELETE_PACKAGES", - "android.permission.DEVICE_POWER", - "android.permission.DIAGNOSTIC", - "android.permission.DUMP", - "android.permission.FACTORY_TEST", - "android.permission.FORCE_BACK", - "android.permission.FORCE_STOP_PACKAGES", - "android.permission.GLOBAL_SEARCH", - "android.permission.GLOBAL_SEARCH_CONTROL", - "android.permission.HARDWARE_TEST", - "android.permission.INJECT_EVENTS", - "android.permission.INSTALL_LOCATION_PROVIDER", - "android.permission.INSTALL_PACKAGES", - "android.permission.INTERNAL_SYSTEM_WINDOW", - "android.permission.MANAGE_APP_TOKENS", - "android.permission.MANAGE_NETWORK_POLICY", - "android.permission.MANAGE_USB", - "android.permission.MASTER_CLEAR", - "android.permission.MODIFY_NETWORK_ACCOUNTING", - "android.permission.MODIFY_PHONE_STATE", - "android.permission.MOVE_PACKAGE", - "android.permission.NET_ADMIN", - "android.permission.MODIFY_PHONE_STATE", - "android.permission.PACKAGE_USAGE_STATS", - "android.permission.PACKAGE_VERIFICATION_AGENT", - "android.permission.PERFORM_CDMA_PROVISIONING", - "android.permission.READ_FRAME_BUFFER", - "android.permission.READ_INPUT_STATE", - "android.permission.READ_NETWORK_USAGE_HISTORY", - "android.permission.READ_PRIVILEGED_PHONE_STATE", - "android.permission.REBOOT", - "android.permission.RECEIVE_EMERGENCY_BROADCAST", - "android.permission.REMOVE_TASKS", - "android.permission.RETRIEVE_WINDOW_CONTENT", - "android.permission.SEND_SMS_NO_CONFIRMATION", - "android.permission.SET_ACTIVITY_WATCHER", - "android.permission.SET_ORIENTATION", - "android.permission.SET_POINTER_SPEED", - "android.permission.SET_PREFERRED_APPLICATIONS", - "android.permission.SET_SCREEN_COMPATIBILITY", - "android.permission.SET_TIME", - "android.permission.SET_WALLPAPER_COMPONENT", - "android.permission.SHUTDOWN", - "android.permission.STATUS_BAR", - "android.permission.STATUS_BAR_SERVICE", - "android.permission.STOP_APP_SWITCHES", - "android.permission.UPDATE_DEVICE_STATS", - "android.permission.WRITE_APN_SETTINGS", - "android.permission.WRITE_GSERVICES", - "android.permission.WRITE_MEDIA_STORAGE", - "android.permission.WRITE_SECURE_SETTINGS" - }; - - /** Constructs a new {@link SystemPermissionsDetector} check */ - public SystemPermissionsDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return file.getName().equals(ANDROID_MANIFEST_XML); - } - - // ---- Implements Detector.XmlScanner ---- - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList(TAG_USES_PERMISSION); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - Attr nameNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME); - if (nameNode != null) { - String permissionName = nameNode.getValue(); - if (Arrays.binarySearch(SYSTEM_PERMISSIONS, permissionName) >= 0) { - context.report(ISSUE, element, context.getLocation(nameNode), - "Permission is only granted to system apps", null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TextFieldDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TextFieldDetector.java deleted file mode 100644 index a059f5c..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TextFieldDetector.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_HINT; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_INPUT_METHOD; -import static com.android.SdkConstants.ATTR_INPUT_TYPE; -import static com.android.SdkConstants.ATTR_PASSWORD; -import static com.android.SdkConstants.ATTR_PHONE_NUMBER; -import static com.android.SdkConstants.ATTR_STYLE; -import static com.android.SdkConstants.EDIT_TEXT; -import static com.android.SdkConstants.ID_PREFIX; -import static com.android.SdkConstants.NEW_ID_PREFIX; - -import com.android.annotations.NonNull; -import com.android.annotations.VisibleForTesting; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -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.android.tools.lint.detector.api.XmlContext; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; - -import java.util.Collection; -import java.util.Collections; - -/** - * Checks for usability problems in text fields: omitting inputType, or omitting a hint. - */ -public class TextFieldDetector extends LayoutDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "TextFields", //$NON-NLS-1$ - "Looks for text fields missing inputType or hint settings", - - "Providing an `inputType` attribute on a text field improves usability " + - "because depending on the data to be input, optimized keyboards can be shown " + - "to the user (such as just digits and parentheses for a phone number). Similarly," + - "a hint attribute displays a hint to the user for what is expected in the " + - "text field.\n" + - "\n" + - "The lint detector also looks at the `id` of the view, and if the id offers a " + - "hint of the purpose of the field (for example, the `id` contains the phrase " + - "`phone` or `email`), then lint will also ensure that the `inputType` contains " + - "the corresponding type attributes.\n" + - "\n" + - "If you really want to keep the text field generic, you can suppress this warning " + - "by setting `inputType=\"text\"`.", - - Category.USABILITY, - 5, - Severity.WARNING, - TextFieldDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new {@link TextFieldDetector} */ - public TextFieldDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList(EDIT_TEXT); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String style = element.getAttribute(ATTR_STYLE); - if (style != null && !style.isEmpty()) { - // The input type might be specified via a style. This will require - // us to track these (similar to what is done for the - // RequiredAttributeDetector to track layout_width and layout_height - // in style declarations). For now, simply ignore these elements - // to avoid producing false positives. - return; - } - - Attr inputTypeNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_INPUT_TYPE); - if (inputTypeNode == null && - !element.hasAttributeNS(ANDROID_URI, ATTR_HINT)) { - // Also make sure the EditText does not set an inputMethod in which case - // an inputType might be provided from the input. - if (element.hasAttributeNS(ANDROID_URI, ATTR_INPUT_METHOD)) { - return; - } - - context.report(ISSUE, element, context.getLocation(element), - "This text field does not specify an inputType or a hint", null); - } - - Attr idNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_ID); - if (idNode == null) { - return; - } - String id = idNode.getValue(); - if (id.isEmpty()) { - return; - } - if (id.startsWith("editText")) { //$NON-NLS-1$ - // Just the default label - return; - } - - String inputType = ""; - if (inputTypeNode != null) { - inputType = inputTypeNode.getValue(); - } - - // TODO: See if the name is just the default names (button1, editText1 etc) - // and if so, do nothing - // TODO: Unit test this - - if (containsWord(id, "phone", true, true)) { //$NON-NLS-1$ - if (!inputType.contains("phone") //$NON-NLS-1$ - && element.getAttributeNodeNS(ANDROID_URI, ATTR_PHONE_NUMBER) == null) { - String message = String.format("The view name (%1$s) suggests this is a phone " - + "number, but it does not include 'phone' in the inputType", id); - reportMismatch(context, idNode, inputTypeNode, message); - } - return; - } - - if (containsWord(id, "width", false, true) - || containsWord(id, "height", false, true) - || containsWord(id, "size", false, true) - || containsWord(id, "length", false, true) - || containsWord(id, "weight", false, true) - || containsWord(id, "number", false, true)) { - if (!inputType.contains("number") && !inputType.contains("phone")) { //$NON-NLS-1$ - String message = String.format("The view name (%1$s) suggests this is a number, " - + "but it does not include a numeric inputType (such as 'numberSigned')", - id); - reportMismatch(context, idNode, inputTypeNode, message); - } - return; - } - - if (containsWord(id, "password", true, true)) { //$NON-NLS-1$ - if (!(inputType.contains("Password")) //$NON-NLS-1$ - && element.getAttributeNodeNS(ANDROID_URI, ATTR_PASSWORD) == null) { - String message = String.format("The view name (%1$s) suggests this is a password, " - + "but it does not include 'textPassword' in the inputType", id); - reportMismatch(context, idNode, inputTypeNode, message); - } - return; - } - - if (containsWord(id, "email", true, true)) { //$NON-NLS-1$ - if (!inputType.contains("Email")) { //$NON-NLS-1$ - String message = String.format("The view name (%1$s) suggests this is an e-mail " - + "address, but it does not include 'textEmail' in the inputType", id); - reportMismatch(context, idNode, inputTypeNode, message); - } - return; - } - - if (endsWith(id, "pin", false, true)) { //$NON-NLS-1$ - if (!(inputType.contains("numberPassword")) //$NON-NLS-1$ - && element.getAttributeNodeNS(ANDROID_URI, ATTR_PASSWORD) == null) { - String message = String.format("The view name (%1$s) suggests this is a password, " - + "but it does not include 'numberPassword' in the inputType", id); - reportMismatch(context, idNode, inputTypeNode, message); - } - return; - } - - if ((containsWord(id, "uri") || containsWord(id, "url")) - && !inputType.contains("textUri")) { - String message = String.format("The view name (%1$s) suggests this is a URI, " - + "but it does not include 'textUri' in the inputType", id); - reportMismatch(context, idNode, inputTypeNode, message); - } - - if ((containsWord(id, "date")) //$NON-NLS-1$ - && !inputType.contains("date")) { //$NON-NLS-1$ - String message = String.format("The view name (%1$s) suggests this is a date, " - + "but it does not include 'date' or 'datetime' in the inputType", id); - reportMismatch(context, idNode, inputTypeNode, message); - } - } - - private static void reportMismatch(XmlContext context, Attr idNode, Attr inputTypeNode, - String message) { - Location location; - if (inputTypeNode != null) { - location = context.getLocation(inputTypeNode); - Location secondary = context.getLocation(idNode); - secondary.setMessage("id defined here"); - location.setSecondary(secondary); - } else { - location = context.getLocation(idNode); - } - context.report(ISSUE, idNode.getOwnerElement(), location, message, null); - } - - /** Returns true if the given sentence contains a given word */ - @VisibleForTesting - static boolean containsWord(String sentence, String word) { - return containsWord(sentence, word, false, false); - } - - /** - * Returns true if the given sentence contains a given word - * @param sentence the full sentence to search within - * @param word the word to look for - * @param allowPrefix if true, allow a prefix match even if the next character - * is in the same word (same case or not an underscore) - * @param allowSuffix if true, allow a suffix match even if the preceding character - * is in the same word (same case or not an underscore) - * @return true if the word is contained in the sentence - */ - @VisibleForTesting - static boolean containsWord(String sentence, String word, boolean allowPrefix, - boolean allowSuffix) { - return indexOfWord(sentence, word, allowPrefix, allowSuffix) != -1; - } - - /** Returns true if the given sentence <b>ends</b> with a given word */ - private static boolean endsWith(String sentence, String word, boolean allowPrefix, - boolean allowSuffix) { - int index = indexOfWord(sentence, word, allowPrefix, allowSuffix); - - if (index != -1) { - return index == sentence.length() - word.length(); - } - - return false; - } - - /** - * Returns the index of the given word in the given sentence, if any. It will match - * across cases, and ignore words that seem to be just a substring in the middle - * of another word. - * - * @param sentence the full sentence to search within - * @param word the word to look for - * @param allowPrefix if true, allow a prefix match even if the next character - * is in the same word (same case or not an underscore) - * @param allowSuffix if true, allow a suffix match even if the preceding character - * is in the same word (same case or not an underscore) - * @return true if the word is contained in the sentence - */ - private static int indexOfWord(String sentence, String word, boolean allowPrefix, - boolean allowSuffix) { - if (sentence.isEmpty()) { - return -1; - } - int wordLength = word.length(); - if (wordLength > sentence.length()) { - return -1; - } - - char firstUpper = Character.toUpperCase(word.charAt(0)); - char firstLower = Character.toLowerCase(firstUpper); - - int start = 0; - if (sentence.startsWith(NEW_ID_PREFIX)) { - start += NEW_ID_PREFIX.length(); - } else if (sentence.startsWith(ID_PREFIX)) { - start += ID_PREFIX.length(); - } - - for (int i = start, n = sentence.length(), m = n - (wordLength - 1); i < m; i++) { - char c = sentence.charAt(i); - if (c == firstUpper || c == firstLower) { - if (sentence.regionMatches(true, i, word, 0, wordLength)) { - if (i <= start && allowPrefix) { - return i; - } - if (i == m - 1 && allowSuffix) { - return i; - } - if (i <= start || (sentence.charAt(i - 1) == '_') - || Character.isUpperCase(c)) { - if (i == m - 1) { - return i; - } - char after = sentence.charAt(i + wordLength); - if (after == '_' || Character.isUpperCase(after)) { - return i; - } - } - } - } - } - - return -1; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TextViewDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TextViewDetector.java deleted file mode 100644 index 3e519cf..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TextViewDetector.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * 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.ANDROID_URI; -import static com.android.SdkConstants.ATTR_AUTO_TEXT; -import static com.android.SdkConstants.ATTR_BUFFER_TYPE; -import static com.android.SdkConstants.ATTR_CAPITALIZE; -import static com.android.SdkConstants.ATTR_CURSOR_VISIBLE; -import static com.android.SdkConstants.ATTR_DIGITS; -import static com.android.SdkConstants.ATTR_EDITABLE; -import static com.android.SdkConstants.ATTR_EDITOR_EXTRAS; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_IME_ACTION_ID; -import static com.android.SdkConstants.ATTR_IME_ACTION_LABEL; -import static com.android.SdkConstants.ATTR_IME_OPTIONS; -import static com.android.SdkConstants.ATTR_INPUT_METHOD; -import static com.android.SdkConstants.ATTR_INPUT_TYPE; -import static com.android.SdkConstants.ATTR_NUMERIC; -import static com.android.SdkConstants.ATTR_PASSWORD; -import static com.android.SdkConstants.ATTR_PHONE_NUMBER; -import static com.android.SdkConstants.ATTR_PRIVATE_IME_OPTIONS; -import static com.android.SdkConstants.ATTR_TEXT; -import static com.android.SdkConstants.ATTR_TEXT_IS_SELECTABLE; -import static com.android.SdkConstants.ATTR_VISIBILITY; -import static com.android.SdkConstants.BUTTON; -import static com.android.SdkConstants.CHECKED_TEXT_VIEW; -import static com.android.SdkConstants.CHECK_BOX; -import static com.android.SdkConstants.RADIO_BUTTON; -import static com.android.SdkConstants.SWITCH; -import static com.android.SdkConstants.TEXT_VIEW; -import static com.android.SdkConstants.TOGGLE_BUTTON; -import static com.android.SdkConstants.VALUE_EDITABLE; -import static com.android.SdkConstants.VALUE_NONE; -import static com.android.SdkConstants.VALUE_TRUE; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -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.android.tools.lint.detector.api.XmlContext; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; - -import java.util.Arrays; -import java.util.Collection; - -/** - * Checks for cases where a TextView should probably be an EditText instead - */ -public class TextViewDetector extends LayoutDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "TextViewEdits", //$NON-NLS-1$ - "Looks for TextViews being used for input", - - "Using a `<TextView>` to input text is generally an error, you should be " + - "using `<EditText>` instead. `EditText` is a subclass of `TextView`, and some " + - "of the editing support is provided by `TextView`, so it's possible to set " + - "some input-related properties on a `TextView`. However, using a `TextView` " + - "along with input attributes is usually a cut & paste error. To input " + - "text you should be using `<EditText>`." + - "\n" + - "This check also checks subclasses of `TextView`, such as `Button` and `CheckBox`, " + - "since these have the same issue: they should not be used with editable " + - "attributes.", - - Category.CORRECTNESS, - 7, - Severity.WARNING, - TextViewDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Text could be selectable */ - public static final Issue SELECTABLE = Issue.create( - "SelectableText", //$NON-NLS-1$ - "Looks for TextViews which should probably allow their text to be selected", - - "If a `<TextView>` is used to display data, the user might want to copy that " + - "data and paste it elsewhere. To allow this, the `<TextView>` should specify " + - "`android:textIsSelectable=\"true\"`.\n" + - "\n" + - "This lint check looks for TextViews which are likely to be displaying data: " + - "views whose text is set dynamically. This value will be ignored on platforms " + - "older than API 11, so it is okay to set it regardless of your `minSdkVersion`.", - - Category.USABILITY, - 7, - Severity.WARNING, - TextViewDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new {@link TextViewDetector} */ - public TextViewDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - TEXT_VIEW, - BUTTON, - TOGGLE_BUTTON, - CHECK_BOX, - RADIO_BUTTON, - CHECKED_TEXT_VIEW, - SWITCH - ); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (element.getTagName().equals(TEXT_VIEW)) { - if (!element.hasAttributeNS(ANDROID_URI, ATTR_TEXT) - && element.hasAttributeNS(ANDROID_URI, ATTR_ID) - && !element.hasAttributeNS(ANDROID_URI, ATTR_TEXT_IS_SELECTABLE) - && !element.hasAttributeNS(ANDROID_URI, ATTR_VISIBILITY) - && context.getMainProject().getTargetSdk() >= 11) { - context.report(SELECTABLE, element, context.getLocation(element), - "Consider making the text value selectable by specifying " + - "android:textIsSelectable=\"true\"", null); - } - } - - NamedNodeMap attributes = element.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Attr attribute = (Attr) attributes.item(i); - String name = attribute.getLocalName(); - if (name == null) { - // Attribute not in a namespace; we only care about the android: ones - continue; - } - - boolean isEditAttribute = false; - switch (name.charAt(0)) { - case 'a': { - isEditAttribute = name.equals(ATTR_AUTO_TEXT); - break; - } - case 'b': { - isEditAttribute = name.equals(ATTR_BUFFER_TYPE) && - attribute.getValue().equals(VALUE_EDITABLE); - break; - } - case 'p': { - isEditAttribute = name.equals(ATTR_PASSWORD) - || name.equals(ATTR_PHONE_NUMBER) - || name.equals(ATTR_PRIVATE_IME_OPTIONS); - break; - } - case 'c': { - isEditAttribute = name.equals(ATTR_CAPITALIZE) - || name.equals(ATTR_CURSOR_VISIBLE); - break; - } - case 'd': { - isEditAttribute = name.equals(ATTR_DIGITS); - break; - } - case 'e': { - if (name.equals(ATTR_EDITABLE)) { - isEditAttribute = attribute.getValue().equals(VALUE_TRUE); - } else { - isEditAttribute = name.equals(ATTR_EDITOR_EXTRAS); - } - break; - } - case 'i': { - if (name.equals(ATTR_INPUT_TYPE)) { - String value = attribute.getValue(); - isEditAttribute = !value.isEmpty() && !value.equals(VALUE_NONE); - } else { - isEditAttribute = name.equals(ATTR_INPUT_TYPE) - || name.equals(ATTR_IME_OPTIONS) - || name.equals(ATTR_IME_ACTION_LABEL) - || name.equals(ATTR_IME_ACTION_ID) - || name.equals(ATTR_INPUT_METHOD); - } - break; - } - case 'n': { - isEditAttribute = name.equals(ATTR_NUMERIC); - break; - } - } - - if (isEditAttribute && ANDROID_URI.equals(attribute.getNamespaceURI())) { - Location location = context.getLocation(attribute); - String message; - String view = element.getTagName(); - if (view.equals(TEXT_VIEW)) { - message = String.format( - "Attribute %1$s should not be used with <TextView>: " + - "Change element type to <EditText> ?", attribute.getName()); - } else { - message = String.format( - "Attribute %1$s should not be used with <%2$s>: " + - "intended for editable text widgets", - attribute.getName(), view); - } - context.report(ISSUE, attribute, location, message, null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TitleDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TitleDetector.java deleted file mode 100644 index 5fc5340..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TitleDetector.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.ANDROID_URI; -import static com.android.SdkConstants.ATTR_TITLE; -import static com.android.SdkConstants.ATTR_VISIBLE; -import static com.android.SdkConstants.TAG_ITEM; -import static com.android.SdkConstants.VALUE_FALSE; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Detector.JavaScanner; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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; -import java.util.Collections; - -/** - * Check which makes sure menu items specify a title - */ -public class TitleDetector extends ResourceXmlDetector implements JavaScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "MenuTitle", //$NON-NLS-1$ - "Ensures that all menu items supply a title", - - "From the action bar documentation:\n" + - // u2014: em dash - "\"It's important that you always define android:title for each menu item \u2014 " + - "even if you don't declare that the title appear with the action item \u2014 for " + - "three reasons:\n" + - "\n" + - "* If there's not enough room in the action bar for the action item, the menu " + - "item appears in the overflow menu and only the title appears.\n" + - "* Screen readers for sight-impaired users read the menu item's title.\n" + - "* If the action item appears with only the icon, a user can long-press the item " + - "to reveal a tool-tip that displays the action item's title.\n" + - "The android:icon is always optional, but recommended.", - - Category.USABILITY, - 5, - Severity.WARNING, - TitleDetector.class, - Scope.RESOURCE_FILE_SCOPE).setMoreInfo( - "http://developer.android.com/guide/topics/ui/actionbar.html"); //$NON-NLS-1$ - - /** Constructs a new {@link TitleDetector} */ - public TitleDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.MENU; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - @Nullable - public Collection<String> getApplicableElements() { - return Collections.singletonList(TAG_ITEM); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (element.hasAttributeNS(ANDROID_URI, ATTR_TITLE)) { - return; - } - - // TODO: Find out if this is necessary on older versions too. - // I swear I saw it mentioned. - if (context.getMainProject().getTargetSdk() < 11) { - return; - } - - if (VALUE_FALSE.equals(element.getAttributeNS(ANDROID_URI, ATTR_VISIBLE))) { - return; - } - - String message = "Menu items should specify a title"; - context.report(ISSUE, element, context.getLocation(element), message, null); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ToastDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ToastDetector.java deleted file mode 100644 index e245fe8..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ToastDetector.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.Scope; -import com.android.tools.lint.detector.api.Severity; - -import java.io.File; -import java.util.Collections; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.IntegralLiteral; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.Return; -import lombok.ast.StrictListAccessor; - -/** Detector looking for Toast.makeText() without a corresponding show() call */ -public class ToastDetector extends Detector implements Detector.JavaScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "ShowToast", //$NON-NLS-1$ - "Looks for code creating a Toast but forgetting to call show() on it", - - "`Toast.makeText()` creates a `Toast` but does *not* show it. You must call " + - "`show()` on the resulting object to actually make the `Toast` appear.", - - Category.CORRECTNESS, - 6, - Severity.WARNING, - ToastDetector.class, - Scope.JAVA_FILE_SCOPE); - - - /** Constructs a new {@link ToastDetector} check */ - public ToastDetector() { - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - - // ---- Implements JavaScanner ---- - - @Override - public List<String> getApplicableMethodNames() { - return Collections.singletonList("makeText"); //$NON-NLS-1$ - } - - @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - assert node.astName().astValue().equals("makeText"); - if (node.astOperand() == null) { - // "makeText()" in the code with no operand - return; - } - - String operand = node.astOperand().toString(); - if (!(operand.equals("Toast") || operand.endsWith(".Toast"))) { - return; - } - - // Make sure you pass the right kind of duration: it's not a delay, it's - // LENGTH_SHORT or LENGTH_LONG - // (see http://code.google.com/p/android/issues/detail?id=3655) - StrictListAccessor<Expression, MethodInvocation> args = node.astArguments(); - if (args.size() == 3) { - Expression duration = args.last(); - if (duration instanceof IntegralLiteral) { - context.report(ISSUE, duration, context.getLocation(duration), - "Expected duration Toast.LENGTH_SHORT or Toast.LENGTH_LONG, a custom " + - "duration value is not supported", - null); - } - } - - Node method = JavaContext.findSurroundingMethod(node.getParent()); - if (method == null) { - return; - } - - ShowFinder finder = new ShowFinder(node); - method.accept(finder); - if (!finder.isShowCalled()) { - context.report(ISSUE, method, context.getLocation(node), - "Toast created but not shown: did you forget to call show() ?", null); - } - } - - private static class ShowFinder extends ForwardingAstVisitor { - /** The target makeText call */ - private final MethodInvocation mTarget; - /** Whether we've found the show method */ - private boolean mFound; - /** Whether we've seen the target makeText node yet */ - private boolean mSeenTarget; - - private ShowFinder(MethodInvocation target) { - mTarget = target; - } - - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (node == mTarget) { - mSeenTarget = true; - } else if ((mSeenTarget || node.astOperand() == mTarget) - && "show".equals(node.astName().astValue())) { //$NON-NLS-1$ - // TODO: Do more flow analysis to see whether we're really calling show - // on the right type of object? - mFound = true; - } - - return true; - } - - @Override - public boolean visitReturn(Return node) { - if (node.astValue() == mTarget) { - // If you just do "return Toast.makeText(...) don't warn - mFound = true; - } - return super.visitReturn(node); - } - - boolean isShowCalled() { - return mFound; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java deleted file mode 100644 index 94a9611..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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.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 root FrameLayout can be replaced with a {@code <merge>} tag. - */ -public class TooManyViewsDetector extends LayoutDetector { - /** Issue of having too many views in a single layout */ - public static final Issue TOO_MANY = Issue.create( - "TooManyViews", //$NON-NLS-1$ - "Checks whether a 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, - TooManyViewsDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Issue of having too deep hierarchies in layouts */ - public static final Issue TOO_DEEP = Issue.create( - "TooDeepLayout", //$NON-NLS-1$ - "Checks whether a 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, - TooManyViewsDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - 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, null); - } - 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, null); - } - } - - @Override - public void visitElementAfter(@NonNull XmlContext context, @NonNull Element element) { - mDepth--; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TranslationDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TranslationDetector.java deleted file mode 100644 index 0f892b1..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TranslationDetector.java +++ /dev/null @@ -1,581 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_PREFIX; -import static com.android.SdkConstants.ATTR_LOCALE; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_TRANSLATABLE; -import static com.android.SdkConstants.STRING_PREFIX; -import static com.android.SdkConstants.TAG_ITEM; -import static com.android.SdkConstants.TAG_STRING; -import static com.android.SdkConstants.TAG_STRING_ARRAY; -import static com.android.SdkConstants.TOOLS_URI; - -import com.android.annotations.NonNull; -import com.android.annotations.VisibleForTesting; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; - -/** - * Checks for incomplete translations - e.g. keys that are only present in some - * locales but not all. - */ -public class TranslationDetector extends ResourceXmlDetector { - @VisibleForTesting - static boolean sCompleteRegions = - System.getenv("ANDROID_LINT_COMPLETE_REGIONS") != null; //$NON-NLS-1$ - - private static final Pattern LANGUAGE_PATTERN = Pattern.compile("^[a-z]{2}$"); //$NON-NLS-1$ - private static final Pattern REGION_PATTERN = Pattern.compile("^r([A-Z]{2})$"); //$NON-NLS-1$ - - /** Are all translations complete? */ - public static final Issue MISSING = Issue.create( - "MissingTranslation", //$NON-NLS-1$ - "Checks for incomplete translations where not all strings are translated", - "If an application has more than one locale, then all the strings declared in " + - "one language should also be translated in all other languages.\n" + - "\n" + - "If the string should *not* be translated, you can add the attribute " + - "`translatable=\"false\"` on the `<string>` element, or you can define all " + - "your non-translatable strings in a resource file called `donottranslate.xml`. " + - "Or, you can ignore the issue with a `tools:ignore=\"MissingTranslation\"` " + - "attribute.\n" + - "\n" + - "By default this detector allows regions of a language to just provide a " + - "subset of the strings and fall back to the standard language strings. " + - "You can require all regions to provide a full translation by setting the " + - "environment variable `ANDROID_LINT_COMPLETE_REGIONS`.\n" + - "\n" + - "You can tell lint (and other tools) which language is the default language " + - "in your `res/values/` folder by specifying `tools:locale=\"languageCode\"` for " + - "the root `<resources>` element in your resource file. (The `tools` prefix refers " + - "to the namespace declaration `http://schemas.android.com/tools`.)", - Category.MESSAGES, - 8, - Severity.FATAL, - TranslationDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Are there extra translations that are "unused" (appear only in specific languages) ? */ - public static final Issue EXTRA = Issue.create( - "ExtraTranslation", //$NON-NLS-1$ - "Checks for translations that appear to be unused (no default language string)", - "If a string appears in a specific language translation file, but there is " + - "no corresponding string in the default locale, then this string is probably " + - "unused. (It's technically possible that your application is only intended to " + - "run in a specific locale, but it's still a good idea to provide a fallback.).\n" + - "\n" + - "Note that these strings can lead to crashes if the string is looked up on any " + - "locale not providing a translation, so it's important to clean them up.", - Category.MESSAGES, - 6, - Severity.FATAL, - TranslationDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - private Set<String> mNames; - private Set<String> mTranslatedArrays; - private Set<String> mNonTranslatable; - private boolean mIgnoreFile; - private Map<File, Set<String>> mFileToNames; - private Map<File, String> mFileToLocale; - - /** Locations for each untranslated string name. Populated during phase 2, if necessary */ - private Map<String, Location> mMissingLocations; - - /** Locations for each extra translated string name. Populated during phase 2, if necessary */ - private Map<String, Location> mExtraLocations; - - /** Error messages for each untranslated string name. Populated during phase 2, if necessary */ - private Map<String, String> mDescriptions; - - /** Constructs a new {@link TranslationDetector} */ - public TranslationDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.VALUES; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - TAG_STRING, - TAG_STRING_ARRAY - ); - } - - @Override - public void beforeCheckProject(@NonNull Context context) { - if (context.getDriver().getPhase() == 1) { - mFileToNames = new HashMap<File, Set<String>>(); - } - } - - @Override - public void beforeCheckFile(@NonNull Context context) { - if (context.getPhase() == 1) { - mNames = new HashSet<String>(); - } - - // Convention seen in various projects - mIgnoreFile = context.file.getName().startsWith("donottranslate") //$NON-NLS-1$ - || UnusedResourceDetector.isAnalyticsFile(context); - - if (!context.getProject().getReportIssues()) { - mIgnoreFile = true; - } - } - - @Override - public void afterCheckFile(@NonNull Context context) { - if (context.getPhase() == 1) { - // Store this layout's set of ids for full project analysis in afterCheckProject - if (context.getProject().getReportIssues() && mNames != null) { - mFileToNames.put(context.file, mNames); - - Element root = ((XmlContext) context).document.getDocumentElement(); - if (root != null) { - String locale = root.getAttributeNS(TOOLS_URI, ATTR_LOCALE); - if (locale != null && !locale.isEmpty()) { - if (mFileToLocale == null) { - mFileToLocale = Maps.newHashMap(); - } - mFileToLocale.put(context.file, locale); - } - } - } - - mNames = null; - } - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (context.getPhase() == 1) { - // NOTE - this will look for the presence of translation strings. - // If you create a resource folder but don't actually place a file in it - // we won't detect that, but it seems like a smaller problem. - - checkTranslations(context); - - mFileToNames = null; - - if (mMissingLocations != null || mExtraLocations != null) { - context.getDriver().requestRepeat(this, Scope.ALL_RESOURCES_SCOPE); - } - } else { - assert context.getPhase() == 2; - - reportMap(context, MISSING, mMissingLocations); - reportMap(context, EXTRA, mExtraLocations); - mMissingLocations = null; - mExtraLocations = null; - mDescriptions = null; - } - } - - private void reportMap(Context context, Issue issue, Map<String, Location> map) { - if (map != null) { - for (Map.Entry<String, Location> entry : map.entrySet()) { - Location location = entry.getValue(); - String name = entry.getKey(); - String message = mDescriptions.get(name); - - // We were prepending locations, but we want to prefer the base folders - location = Location.reverse(location); - - context.report(issue, location, message, null); - } - } - } - - private void checkTranslations(Context context) { - // Only one file defining strings? If so, no problems. - Set<File> files = mFileToNames.keySet(); - if (files.size() == 1) { - return; - } - - Set<File> parentFolders = new HashSet<File>(); - for (File file : files) { - parentFolders.add(file.getParentFile()); - } - if (parentFolders.size() == 1) { - // Only one language - no problems. - return; - } - - boolean reportMissing = context.isEnabled(MISSING); - boolean reportExtra = context.isEnabled(EXTRA); - - // res/strings.xml etc - String defaultLanguage = "Default"; - - Map<File, String> parentFolderToLanguage = new HashMap<File, String>(); - for (File parent : parentFolders) { - String name = parent.getName(); - - // Look up the language for this folder. - String language = getLanguage(name); - if (language == null) { - language = defaultLanguage; - } - - parentFolderToLanguage.put(parent, language); - } - - int languageCount = parentFolderToLanguage.values().size(); - if (languageCount <= 1) { - // At most one language -- no problems. - return; - } - - // Merge together the various files building up the translations for each language - Map<String, Set<String>> languageToStrings = - new HashMap<String, Set<String>>(languageCount); - Set<String> allStrings = new HashSet<String>(200); - for (File file : files) { - String language = null; - if (mFileToLocale != null) { - String locale = mFileToLocale.get(file); - if (locale != null) { - int index = locale.indexOf('-'); - if (index != -1) { - locale = locale.substring(0, index); - } - language = locale; - } - } - if (language == null) { - language = parentFolderToLanguage.get(file.getParentFile()); - } - assert language != null : file.getParent(); - Set<String> fileStrings = mFileToNames.get(file); - - Set<String> languageStrings = languageToStrings.get(language); - if (languageStrings == null) { - // We don't need a copy; we're done with the string tables now so we - // can modify them - languageToStrings.put(language, fileStrings); - } else { - languageStrings.addAll(fileStrings); - } - allStrings.addAll(fileStrings); - } - - Set<String> defaultStrings = languageToStrings.get(defaultLanguage); - if (defaultStrings == null) { - defaultStrings = new HashSet<String>(); - } - - // Fast check to see if there's no problem: if the default locale set is the - // same as the all set (meaning there are no extra strings in the other languages) - // then we can quickly determine if everything is okay by just making sure that - // each language defines everything. If that's the case they will all have the same - // string count. - int stringCount = allStrings.size(); - if (stringCount == defaultStrings.size()) { - boolean haveError = false; - for (Map.Entry<String, Set<String>> entry : languageToStrings.entrySet()) { - Set<String> strings = entry.getValue(); - if (stringCount != strings.size()) { - haveError = true; - break; - } - } - if (!haveError) { - return; - } - } - - // Do we need to resolve fallback strings for regions that only define a subset - // of the strings in the language and fall back on the main language for the rest? - if (!sCompleteRegions) { - for (String l : languageToStrings.keySet()) { - if (l.indexOf('-') != -1) { - // Yes, we have regions. Merge all base language string names into each region. - for (Map.Entry<String, Set<String>> entry : languageToStrings.entrySet()) { - Set<String> strings = entry.getValue(); - if (stringCount != strings.size()) { - String languageRegion = entry.getKey(); - int regionIndex = languageRegion.indexOf('-'); - if (regionIndex != -1) { - String language = languageRegion.substring(0, regionIndex); - Set<String> fallback = languageToStrings.get(language); - if (fallback != null) { - strings.addAll(fallback); - } - } - } - } - // We only need to do this once; when we see the first region we know - // we need to do it; once merged we can bail - break; - } - } - } - - List<String> languages = new ArrayList<String>(languageToStrings.keySet()); - Collections.sort(languages); - for (String language : languages) { - Set<String> strings = languageToStrings.get(language); - if (defaultLanguage.equals(language)) { - continue; - } - - // if strings.size() == stringCount, then this language is defining everything, - // both all the default language strings and the union of all extra strings - // defined in other languages, so there's no problem. - if (stringCount != strings.size()) { - if (reportMissing) { - Set<String> difference = Sets.difference(defaultStrings, strings); - if (!difference.isEmpty()) { - if (mMissingLocations == null) { - mMissingLocations = new HashMap<String, Location>(); - } - if (mDescriptions == null) { - mDescriptions = new HashMap<String, String>(); - } - - for (String s : difference) { - mMissingLocations.put(s, null); - String message = mDescriptions.get(s); - if (message == null) { - message = String.format("\"%1$s\" is not translated in %2$s", - s, language); - } else { - message = message + ", " + language; - } - mDescriptions.put(s, message); - } - } - } - - if (reportExtra) { - Set<String> difference = Sets.difference(strings, defaultStrings); - if (!difference.isEmpty()) { - if (mExtraLocations == null) { - mExtraLocations = new HashMap<String, Location>(); - } - if (mDescriptions == null) { - mDescriptions = new HashMap<String, String>(); - } - - for (String s : difference) { - if (mTranslatedArrays != null && mTranslatedArrays.contains(s)) { - continue; - } - mExtraLocations.put(s, null); - String message = String.format( - "\"%1$s\" is translated here but not found in default locale", s); - mDescriptions.put(s, message); - } - } - } - } - } - } - - /** Look up the language for the given folder name */ - private static String getLanguage(String name) { - String[] segments = name.split("-"); //$NON-NLS-1$ - - // TODO: To get an accurate answer, this should later do a - // FolderConfiguration.getConfig(String[] folderSegments) - // to obtain a FolderConfiguration, then call - // getLanguageQualifier() on it, and if not null, call getValue() to get the - // actual language value. - // However, we don't have sdk_common on the build path for lint, so for now - // use a simple guess about what constitutes a language qualifier here: - - String language = null; - for (String segment : segments) { - // Language - if (language == null && segment.length() == 2 - && LANGUAGE_PATTERN.matcher(segment).matches()) { - language = segment; - } - - // Add in region - if (language != null && segment.length() == 3 - && REGION_PATTERN.matcher(segment).matches()) { - language = language + '-' + segment; - break; - } - } - - return language; - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (mIgnoreFile) { - return; - } - - Attr attribute = element.getAttributeNode(ATTR_NAME); - - if (context.getPhase() == 2) { - // Just locating names requested in the {@link #mLocations} map - if (attribute == null) { - return; - } - String name = attribute.getValue(); - if (mMissingLocations != null && mMissingLocations.containsKey(name)) { - String language = getLanguage(context.file.getParentFile().getName()); - if (language == null) { - if (context.getDriver().isSuppressed(MISSING, element)) { - mMissingLocations.remove(name); - return; - } - - Location location = context.getLocation(attribute); - location.setClientData(element); - location.setSecondary(mMissingLocations.get(name)); - mMissingLocations.put(name, location); - } - } - if (mExtraLocations != null && mExtraLocations.containsKey(name)) { - if (context.getDriver().isSuppressed(EXTRA, element)) { - mExtraLocations.remove(name); - return; - } - Location location = context.getLocation(attribute); - location.setClientData(element); - location.setMessage("Also translated here"); - location.setSecondary(mExtraLocations.get(name)); - mExtraLocations.put(name, location); - } - return; - } - - assert context.getPhase() == 1; - if (attribute == null || attribute.getValue().isEmpty()) { - context.report(MISSING, element, context.getLocation(element), - "Missing name attribute in <string> declaration", null); - } else { - String name = attribute.getValue(); - - Attr translatable = element.getAttributeNode(ATTR_TRANSLATABLE); - if (translatable != null && !Boolean.valueOf(translatable.getValue())) { - String l = LintUtils.getLocaleAndRegion(context.file.getParentFile().getName()); - if (l != null) { - context.report(EXTRA, translatable, context.getLocation(translatable), - "Non-translatable resources should only be defined in the base " + - "values/ folder", null); - } else { - if (mNonTranslatable == null) { - mNonTranslatable = new HashSet<String>(); - } - mNonTranslatable.add(name); - } - return; - } - - if (element.getTagName().equals(TAG_STRING_ARRAY) && - allItemsAreReferences(element)) { - // No need to provide translations for string arrays where all - // the children items are defined as translated string resources, - // e.g. - // <string-array name="foo"> - // <item>@string/item1</item> - // <item>@string/item2</item> - // </string-array> - // However, we need to remember these names such that we don't consider - // these arrays "extra" if one of the *translated* versions of the array - // perform an inline translation of an array item - if (mTranslatedArrays == null) { - mTranslatedArrays = new HashSet<String>(); - } - mTranslatedArrays.add(name); - return; - } - - // Check for duplicate name definitions? No, because there can be - // additional customizations like product= - //if (mNames.contains(name)) { - // context.mClient.report(ISSUE, context.getLocation(attribute), - // String.format("Duplicate name %1$s, already defined earlier in this file", - // name)); - //} - - mNames.add(name); - - if (mNonTranslatable != null && mNonTranslatable.contains(name)) { - String message = String.format("The resource string \"%1$s\" has been marked as " + - "translatable=\"false\"", name); - context.report(EXTRA, attribute, context.getLocation(attribute), message, null); - } - - // TBD: Also make sure that the strings are not empty or placeholders? - } - } - - private static boolean allItemsAreReferences(Element element) { - assert element.getTagName().equals(TAG_STRING_ARRAY); - NodeList childNodes = element.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node item = childNodes.item(i); - if (item.getNodeType() == Node.ELEMENT_NODE && - TAG_ITEM.equals(item.getNodeName())) { - NodeList itemChildren = item.getChildNodes(); - for (int j = 0, m = itemChildren.getLength(); j < m; j++) { - Node valueNode = itemChildren.item(j); - if (valueNode.getNodeType() == Node.TEXT_NODE) { - String value = valueNode.getNodeValue().trim(); - if (!value.startsWith(ANDROID_PREFIX) - && !value.startsWith(STRING_PREFIX)) { - return false; - } - } - } - } - } - - return true; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypoDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypoDetector.java deleted file mode 100644 index b9b889f..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypoDetector.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ATTR_LOCALE; -import static com.android.SdkConstants.FD_RES_VALUES; -import static com.android.SdkConstants.TAG_STRING; -import static com.android.SdkConstants.TOOLS_URI; -import static com.android.tools.lint.checks.TypoLookup.isLetter; -import static com.google.common.base.Objects.equal; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 com.google.common.base.Charsets; -import com.google.common.base.Splitter; - -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Check which looks for likely typos in Strings. - * <p> - * TODO: - * <ul> - * <li> Add check of Java String literals too! - * <li> Add support for <b>additional</b> languages. The typo detector is now - * multilingual and looks for typos-*locale*.txt files to use. However, - * we need to seed it with additional typo databases. I did some searching - * and came up with some alternatives. Here's the strategy I used: - * Used Google Translate to translate "Wikipedia Common Misspellings", and - * then I went to google.no, google.fr etc searching with that translation, and - * came up with what looks like wikipedia language local lists of typos. - * This is how I found the Norwegian one for example: - * <br> - * http://no.wikipedia.org/wiki/Wikipedia:Liste_over_alminnelige_stavefeil/Maskinform - * <br> - * Here are some additional possibilities not yet processed: - * <ul> - * <li> French: http://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Liste_de_fautes_d'orthographe_courantes - * (couldn't find a machine-readable version there?) - * <li> Swedish: - * http://sv.wikipedia.org/wiki/Wikipedia:Lista_%C3%B6ver_vanliga_spr%C3%A5kfel - * (couldn't find a machine-readable version there?) - * <li> German - * http://de.wikipedia.org/wiki/Wikipedia:Liste_von_Tippfehlern/F%C3%BCr_Maschinen - * </ul> - * <li> Consider also digesting files like - * http://sv.wikipedia.org/wiki/Wikipedia:AutoWikiBrowser/Typos - * See http://en.wikipedia.org/wiki/Wikipedia:AutoWikiBrowser/User_manual. - * </ul> - */ -public class TypoDetector extends ResourceXmlDetector { - @Nullable private TypoLookup mLookup; - @Nullable private String mLastLanguage; - @Nullable private String mLastRegion; - @Nullable private String mLanguage; - @Nullable private String mRegion; - - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "Typos", //$NON-NLS-1$ - "Looks for typos in messages", - - "This check looks through the string definitions, and if it finds any words " + - "that look like likely misspellings, they are flagged.", - Category.MESSAGES, - 7, - Severity.WARNING, - TypoDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new detector */ - public TypoDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.VALUES; - } - - /** Look up the locale and region from the given parent folder name and store it - * in {@link #mLanguage} and {@link #mRegion} */ - private void initLocale(@NonNull String parent) { - mLanguage = null; - mRegion = null; - - if (parent.equals(FD_RES_VALUES)) { - return; - } - - for (String qualifier : Splitter.on('-').split(parent)) { - int qualifierLength = qualifier.length(); - if (qualifierLength == 2) { - char first = qualifier.charAt(0); - char second = qualifier.charAt(1); - if (first >= 'a' && first <= 'z' && second >= 'a' && second <= 'z') { - mLanguage = qualifier; - } - } else if (qualifierLength == 3 && qualifier.charAt(0) == 'r') { - char first = qualifier.charAt(1); - char second = qualifier.charAt(2); - if (first >= 'A' && first <= 'Z' && second >= 'A' && second <= 'Z') { - mRegion = new String(new char[] { first, second }); // Don't include the "r" - } - break; - } - } - } - - @Override - public void beforeCheckFile(@NonNull Context context) { - initLocale(context.file.getParentFile().getName()); - if (mLanguage == null) { - // Check to see if the user has specified the language for this folder - // using a tools:locale attribute - if (context instanceof XmlContext) { - Element root = ((XmlContext) context).document.getDocumentElement(); - if (root != null) { - String locale = root.getAttributeNS(TOOLS_URI, ATTR_LOCALE); - if (locale != null && !locale.isEmpty()) { - initLocale(FD_RES_VALUES + '-' + locale); - } - } - } - - if (mLanguage == null) { - mLanguage = "en"; //$NON-NLS-1$ - } - } - - if (!equal(mLastLanguage, mLanguage) || !equal(mLastRegion, mRegion)) { - mLookup = TypoLookup.get(context.getClient(), mLanguage, mRegion); - mLastLanguage = mLanguage; - mLastRegion = mRegion; - } - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.NORMAL; - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList(TAG_STRING); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (mLookup == null) { - return; - } - - visit(context, element); - } - - private void visit(XmlContext context, Node node) { - if (node.getNodeType() == Node.TEXT_NODE) { - // TODO: Figure out how to deal with entities - check(context, node, node.getNodeValue()); - } else { - NodeList children = node.getChildNodes(); - for (int i = 0, n = children.getLength(); i < n; i++) { - visit(context, children.item(i)); - } - } - } - - private void check(XmlContext context, Node node, String text) { - int max = text.length(); - int index = 0; - boolean checkedTypos = false; - while (index < max) { - for (; index < max; index++) { - char c = text.charAt(index); - if (c == '\\') { - index++; - continue; - } else if (Character.isLetter(c)) { - break; - } - } - if (index >= max) { - return; - } - int begin = index; - for (; index < max; index++) { - char c = text.charAt(index); - if (c == '\\') { - index++; - break; - } else if (!Character.isLetter(c)) { - break; - } else if (text.charAt(index) >= 0x80) { - // Switch to UTF-8 handling for this string - if (checkedTypos) { - // If we've already checked words we may have reported typos - // so create a substring from the current word and on. - byte[] utf8Text = text.substring(begin).getBytes(Charsets.UTF_8); - check(context, node, utf8Text, 0, utf8Text.length, text, begin); - } else { - // If all we've done so far is skip whitespace (common scenario) - // then no need to substring the text, just re-search with the - // UTF-8 routines - byte[] utf8Text = text.getBytes(Charsets.UTF_8); - check(context, node, utf8Text, 0, utf8Text.length, text, 0); - } - return; - } - } - - int end = index; - checkedTypos = true; - List<String> replacements = mLookup.getTypos(text, begin, end); - if (replacements != null) { - reportTypo(context, node, text, begin, replacements); - } - - index = end + 1; - } - } - - private void check(XmlContext context, Node node, byte[] utf8Text, - int byteStart, int byteEnd, String text, int charStart) { - int index = byteStart; - while (index < byteEnd) { - // Find beginning of word - while (index < byteEnd) { - byte b = utf8Text[index]; - if (b == '\\') { - index++; - charStart++; - if (index < byteEnd) { - b = utf8Text[index]; - } - } else if (isLetter(b)) { - break; - } - index++; - if ((b & 0x80) == 0 || (b & 0xC0) == 0xC0) { - // First characters in UTF-8 are always ASCII (0 high bit) or 11XXXXXX - charStart++; - } - } - - if (index >= byteEnd) { - return; - } - int charEnd = charStart; - int begin = index; - - // Find end of word. Unicode has the nice property that even 2nd, 3rd and 4th - // bytes won't match these ASCII characters (because the high bit must be set there) - while (index < byteEnd) { - byte b = utf8Text[index]; - if (b == '\\') { - index++; - charEnd++; - if (index < byteEnd) { - b = utf8Text[index++]; - if ((b & 0x80) == 0 || (b & 0xC0) == 0xC0) { - charEnd++; - } - } - break; - } else if (!isLetter(b)) { - break; - } - index++; - if ((b & 0x80) == 0 || (b & 0xC0) == 0xC0) { - // First characters in UTF-8 are always ASCII (0 high bit) or 11XXXXXX - charEnd++; - } - } - - int end = index; - List<String> replacements = mLookup.getTypos(utf8Text, begin, end); - if (replacements != null) { - reportTypo(context, node, text, charStart, replacements); - } - - charStart = charEnd; - } - } - - /** Report the typo found at the given offset and suggest the given replacements */ - private static void reportTypo(XmlContext context, Node node, String text, int begin, - List<String> replacements) { - if (replacements.size() < 2) { - return; - } - - String typo = replacements.get(0); - String word = text.substring(begin, begin + typo.length()); - - String first = null; - String message; - - boolean isCapitalized = Character.isUpperCase(word.charAt(0)); - StringBuilder sb = new StringBuilder(40); - for (int i = 1, n = replacements.size(); i < n; i++) { - String replacement = replacements.get(i); - if (first == null) { - first = replacement; - } - if (sb.length() > 0) { - sb.append(" or "); - } - sb.append('"'); - if (isCapitalized) { - sb.append(Character.toUpperCase(replacement.charAt(0))); - sb.append(replacement.substring(1)); - } else { - sb.append(replacement); - } - sb.append('"'); - } - - if (first != null && first.equalsIgnoreCase(word)) { - if (first.equals(word)) { - return; - } - message = String.format( - "\"%1$s\" is usually capitalized as \"%2$s\"", - word, first); - } else { - message = String.format( - "\"%1$s\" is a common misspelling; did you mean %2$s ?", - word, sb.toString()); - } - - int end = begin + word.length(); - context.report(ISSUE, node, context.getLocation(node, begin, end), message, null); - } - - /** Returns the suggested replacements, if any, for the given typo. The error - * message <b>must</b> be one supplied by lint. - * - * @param errorMessage the error message - * @return a list of replacement words suggested by the error message - */ - @Nullable - public static List<String> getSuggestions(@NonNull String errorMessage) { - // The words are all in quotes; the first word is the misspelling, - // the other words are the suggested replacements - List<String> words = new ArrayList<String>(); - // Skip the typo - int index = errorMessage.indexOf('"'); - index = errorMessage.indexOf('"', index + 1); - index++; - - while (true) { - index = errorMessage.indexOf('"', index); - if (index == -1) { - break; - } - index++; - int start = index; - index = errorMessage.indexOf('"', index); - if (index == -1) { - index = errorMessage.length(); - } - words.add(errorMessage.substring(start, index)); - index++; - } - - return words; - } - - /** - * Returns the typo word in the error message from this detector - * - * @param errorMessage the error message produced earlier by this detector - * @return the typo - */ - @Nullable - public static String getTypo(@NonNull String errorMessage) { - // The words are all in quotes - int index = errorMessage.indexOf('"'); - int start = index + 1; - index = errorMessage.indexOf('"', start); - if (index != -1) { - return errorMessage.substring(start, index); - } - - return null; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypoLookup.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypoLookup.java deleted file mode 100644 index 7d9d8f2..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypoLookup.java +++ /dev/null @@ -1,785 +0,0 @@ -/* - * 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.DOT_XML; -import static com.android.tools.lint.detector.api.LintUtils.assertionsEnabled; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.annotations.VisibleForTesting; -import com.android.tools.lint.client.api.LintClient; -import com.android.tools.lint.detector.api.LintUtils; -import com.google.common.base.Charsets; -import com.google.common.base.Splitter; -import com.google.common.io.Files; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel.MapMode; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.WeakHashMap; - -/** - * Database of common typos / misspellings. - */ -public class TypoLookup { - private static final TypoLookup NONE = new TypoLookup(); - - /** String separating misspellings and suggested replacements in the text file */ - private static final String WORD_SEPARATOR = "->"; //$NON-NLS-1$ - - /** Relative path to the typos database file within the Lint installation */ - private static final String XML_FILE_PATH = "tools/support/typos-%1$s.txt"; //$NON-NLS-1$ - private static final String FILE_HEADER = "Typo database used by Android lint\000"; - private static final int BINARY_FORMAT_VERSION = 2; - private static final boolean DEBUG_FORCE_REGENERATE_BINARY = false; - private static final boolean DEBUG_SEARCH = false; - private static final boolean WRITE_STATS = false; - /** Default size to reserve for each API entry when creating byte buffer to build up data */ - private static final int BYTES_PER_ENTRY = 28; - - private final LintClient mClient; - private final File mXmlFile; - private final File mBinaryFile; - private byte[] mData; - private int[] mIndices; - private int mWordCount; - - private static final WeakHashMap<String, TypoLookup> sInstanceMap = - new WeakHashMap<String, TypoLookup>(); - - /** - * Returns an instance of the Typo database for the given locale - * - * @param client the client to associate with this database - used only for - * logging. The database object may be shared among repeated - * invocations, and in that case client used will be the one - * originally passed in. In other words, this parameter may be - * ignored if the client created is not new. - * @param locale the locale to look up a typo database for (should be a - * language code (ISO 639-1, two lowercase character names) - * @param region the region to look up a typo database for (should be a two - * letter ISO 3166-1 alpha-2 country code in upper case) language - * code - * @return a (possibly shared) instance of the typo database, or null if its - * data can't be found - */ - @Nullable - public static TypoLookup get(@NonNull LintClient client, @NonNull String locale, - @Nullable String region) { - synchronized (TypoLookup.class) { - String key = locale; - - if (region != null) { - // Allow for region-specific dictionaries. See for example - // http://en.wikipedia.org/wiki/American_and_British_English_spelling_differences - assert region.length() == 2 - && Character.isUpperCase(region.charAt(0)) - && Character.isUpperCase(region.charAt(1)) : region; - // Look for typos-en-rUS.txt etc - key = locale + 'r' + region; - } - - TypoLookup db = sInstanceMap.get(key); - if (db == null) { - String path = String.format(XML_FILE_PATH, key); - File file = client.findResource(path); - if (file == null) { - // AOSP build environment? - String build = System.getenv("ANDROID_BUILD_TOP"); //$NON-NLS-1$ - if (build != null) { - file = new File(build, ("sdk/files/" //$NON-NLS-1$ - + path.substring(path.lastIndexOf('/') + 1)) - .replace('/', File.separatorChar)); - } - } - - if (file == null || !file.exists()) { - if (region != null) { - // Fall back to the generic locale (non-region-specific) database - return get(client, locale, null); - } - db = NONE; - } else { - db = get(client, file); - assert db != null : file; - } - sInstanceMap.put(key, db); - } - - if (db == NONE) { - return null; - } else { - return db; - } - } - } - - /** - * Returns an instance of the typo database - * - * @param client the client to associate with this database - used only for - * logging - * @param xmlFile the XML file containing configuration data to use for this - * database - * @return a (possibly shared) instance of the typo database, or null - * if its data can't be found - */ - @Nullable - private static TypoLookup get(LintClient client, File xmlFile) { - if (!xmlFile.exists()) { - client.log(null, "The typo database file %1$s does not exist", xmlFile); - return null; - } - - String name = xmlFile.getName(); - if (LintUtils.endsWith(name, DOT_XML)) { - name = name.substring(0, name.length() - DOT_XML.length()); - } - File cacheDir = client.getCacheDir(true/*create*/); - if (cacheDir == null) { - cacheDir = xmlFile.getParentFile(); - } - - File binaryData = new File(cacheDir, name - // Incorporate version number in the filename to avoid upgrade filename - // conflicts on Windows (such as issue #26663) - + '-' + BINARY_FORMAT_VERSION + ".bin"); //$NON-NLS-1$ - - if (DEBUG_FORCE_REGENERATE_BINARY) { - System.err.println("\nTemporarily regenerating binary data unconditionally \nfrom " - + xmlFile + "\nto " + binaryData); - if (!createCache(client, xmlFile, binaryData)) { - return null; - } - } else if (!binaryData.exists() || binaryData.lastModified() < xmlFile.lastModified()) { - if (!createCache(client, xmlFile, binaryData)) { - return null; - } - } - - if (!binaryData.exists()) { - client.log(null, "The typo database file %1$s does not exist", binaryData); - return null; - } - - return new TypoLookup(client, xmlFile, binaryData); - } - - private static boolean createCache(LintClient client, File xmlFile, File binaryData) { - long begin = 0; - if (WRITE_STATS) { - begin = System.currentTimeMillis(); - } - - // Read in data - List<String> lines; - try { - lines = Files.readLines(xmlFile, Charsets.UTF_8); - } catch (IOException e) { - client.log(e, "Can't read typo database file"); - return false; - } - - if (WRITE_STATS) { - long end = System.currentTimeMillis(); - System.out.println("Reading data structures took " + (end - begin) + " ms)"); - } - - try { - writeDatabase(binaryData, lines); - return true; - } catch (IOException ioe) { - client.log(ioe, "Can't write typo cache file"); - } - - return false; - } - - /** Use one of the {@link #get} factory methods instead */ - private TypoLookup( - @NonNull LintClient client, - @NonNull File xmlFile, - @Nullable File binaryFile) { - mClient = client; - mXmlFile = xmlFile; - mBinaryFile = binaryFile; - - if (binaryFile != null) { - readData(); - } - } - - private TypoLookup() { - mClient = null; - mXmlFile = null; - mBinaryFile = null; - } - - private void readData() { - if (!mBinaryFile.exists()) { - mClient.log(null, "%1$s does not exist", mBinaryFile); - return; - } - long start = System.currentTimeMillis(); - try { - MappedByteBuffer buffer = Files.map(mBinaryFile, MapMode.READ_ONLY); - assert buffer.order() == ByteOrder.BIG_ENDIAN; - - // First skip the header - byte[] expectedHeader = FILE_HEADER.getBytes(Charsets.US_ASCII); - buffer.rewind(); - for (int offset = 0; offset < expectedHeader.length; offset++) { - if (expectedHeader[offset] != buffer.get()) { - mClient.log(null, "Incorrect file header: not an typo database cache " + - "file, or a corrupt cache file"); - return; - } - } - - // Read in the format number - if (buffer.get() != BINARY_FORMAT_VERSION) { - // Force regeneration of new binary data with up to date format - if (createCache(mClient, mXmlFile, mBinaryFile)) { - readData(); // Recurse - } - - return; - } - - mWordCount = buffer.getInt(); - - // Read in the word table indices; - int count = mWordCount; - int[] offsets = new int[count]; - - // Another idea: I can just store the DELTAS in the file (and add them up - // when reading back in) such that it takes just ONE byte instead of four! - - for (int i = 0; i < count; i++) { - offsets[i] = buffer.getInt(); - } - - // No need to read in the rest -- we'll just keep the whole byte array in memory - // TODO: Make this code smarter/more efficient. - int size = buffer.limit(); - byte[] b = new byte[size]; - buffer.rewind(); - buffer.get(b); - mData = b; - mIndices = offsets; - - // TODO: We only need to keep the data portion here since we've initialized - // the offset array separately. - // TODO: Investigate (profile) accessing the byte buffer directly instead of - // accessing a byte array. - } catch (IOException e) { - mClient.log(e, null); - } - if (WRITE_STATS) { - long end = System.currentTimeMillis(); - System.out.println("\nRead typo database in " + (end - start) - + " milliseconds."); - System.out.println("Size of data table: " + mData.length + " bytes (" - + Integer.toString(mData.length/1024) + "k)\n"); - } - } - - /** See the {@link #readData()} for documentation on the data format. */ - private static void writeDatabase(File file, List<String> lines) throws IOException { - /* - * 1. A file header, which is the exact contents of {@link FILE_HEADER} encoded - * as ASCII characters. The purpose of the header is to identify what the file - * is for, for anyone attempting to open the file. - * 2. A file version number. If the binary file does not match the reader's expected - * version, it can ignore it (and regenerate the cache from XML). - */ - - // Drop comments etc - List<String> words = new ArrayList<String>(lines.size()); - for (String line : lines) { - if (!line.isEmpty() && Character.isLetter(line.charAt(0))) { - int end = line.indexOf(WORD_SEPARATOR); - if (end == -1) { - end = line.trim().length(); - } - String typo = line.substring(0, end).trim(); - String replacements = line.substring(end + WORD_SEPARATOR.length()).trim(); - if (replacements.isEmpty()) { - // We don't support empty replacements - continue; - } - String combined = typo + (char) 0 + replacements; - - words.add(combined); - } - } - - byte[][] wordArrays = new byte[words.size()][]; - for (int i = 0, n = words.size(); i < n; i++) { - String word = words.get(i); - wordArrays[i] = word.getBytes(Charsets.UTF_8); - } - // Sort words, using our own comparator to ensure that it matches the - // binary search in getTypos() - Comparator<byte[]> comparator = new Comparator<byte[]>() { - @Override - public int compare(byte[] o1, byte[] o2) { - return TypoLookup.compare(o1, 0, (byte) 0, o2, 0, o2.length); - } - }; - Arrays.sort(wordArrays, comparator); - - int entryCount = wordArrays.length; - int capacity = entryCount * BYTES_PER_ENTRY; - ByteBuffer buffer = ByteBuffer.allocate(capacity); - buffer.order(ByteOrder.BIG_ENDIAN); - // 1. A file header, which is the exact contents of {@link FILE_HEADER} encoded - // as ASCII characters. The purpose of the header is to identify what the file - // is for, for anyone attempting to open the file. - buffer.put(FILE_HEADER.getBytes(Charsets.US_ASCII)); - - // 2. A file version number. If the binary file does not match the reader's expected - // version, it can ignore it (and regenerate the cache from XML). - buffer.put((byte) BINARY_FORMAT_VERSION); - - // 3. The number of words [1 int] - buffer.putInt(entryCount); - - // 4. Word offset table (one integer per word, pointing to the byte offset in the - // file (relative to the beginning of the file) where each word begins. - // The words are always sorted alphabetically. - int wordOffsetTable = buffer.position(); - - // Reserve enough room for the offset table here: we will backfill it with pointers - // as we're writing out the data structures below - for (int i = 0, n = entryCount; i < n; i++) { - buffer.putInt(0); - } - - int nextEntry = buffer.position(); - int nextOffset = wordOffsetTable; - - // 7. Word entry table. Each word entry consists of the word, followed by the byte 0 - // as a terminator, followed by a comma separated list of suggestions (which - // may be empty), or a final 0. - for (int i = 0; i < entryCount; i++) { - byte[] word = wordArrays[i]; - buffer.position(nextOffset); - buffer.putInt(nextEntry); - nextOffset = buffer.position(); - buffer.position(nextEntry); - - buffer.put(word); // already embeds 0 to separate typo from words - buffer.put((byte) 0); - - nextEntry = buffer.position(); - } - - int size = buffer.position(); - assert size <= buffer.limit(); - buffer.mark(); - - if (WRITE_STATS) { - System.out.println("Wrote " + words.size() + " word entries"); - System.out.print("Actual binary size: " + size + " bytes"); - System.out.println(String.format(" (%.1fM)", size/(1024*1024.f))); - - System.out.println("Allocated size: " + (entryCount * BYTES_PER_ENTRY) + " bytes"); - System.out.println("Required bytes per entry: " + (size/ entryCount) + " bytes"); - } - - // Now dump this out as a file - // There's probably an API to do this more efficiently; TODO: Look into this. - byte[] b = new byte[size]; - buffer.rewind(); - buffer.get(b); - FileOutputStream output = Files.newOutputStreamSupplier(file).getOutput(); - output.write(b); - output.close(); - } - - // For debugging only - private String dumpEntry(int offset) { - if (DEBUG_SEARCH) { - int end = offset; - while (mData[end] != 0) { - end++; - } - return new String(mData, offset, end - offset, Charsets.UTF_8); - } else { - return "<disabled>"; //$NON-NLS-1$ - } - } - - /** Comparison function: *only* used for ASCII strings */ - @VisibleForTesting - static int compare(byte[] data, int offset, byte terminator, CharSequence s, - int begin, int end) { - int i = offset; - int j = begin; - for (; ; i++, j++) { - byte b = data[i]; - if (b == ' ') { - // We've matched up to the space in a split-word typo, such as - // in German all zu=>allzu; here we've matched just past "all". - // Rather than terminating, attempt to continue in the buffer. - if (j == end) { - int max = s.length(); - if (end < max && s.charAt(end) == ' ') { - // Find next word - for (; end < max; end++) { - char c = s.charAt(end); - if (!Character.isLetter(c)) { - if (c == ' ' && end == j) { - continue; - } - break; - } - } - } - } - } - - if (j == end) { - break; - } - - if (b == '*') { - // Glob match (only supported at the end) - return 0; - } - char c = s.charAt(j); - byte cb = (byte) c; - int delta = b - cb; - if (delta != 0) { - cb = (byte) Character.toLowerCase(c); - if (b != cb) { - // Ensure that it has the right sign - b = (byte) Character.toLowerCase(b); - delta = b - cb; - if (delta != 0) { - return delta; - } - } - } - } - - return data[i] - terminator; - } - - /** Comparison function used for general UTF-8 encoded strings */ - @VisibleForTesting - static int compare(byte[] data, int offset, byte terminator, byte[] s, - int begin, int end) { - int i = offset; - int j = begin; - for (; ; i++, j++) { - byte b = data[i]; - if (b == ' ') { - // We've matched up to the space in a split-word typo, such as - // in German all zu=>allzu; here we've matched just past "all". - // Rather than terminating, attempt to continue in the buffer. - // We've matched up to the space in a split-word typo, such as - // in German all zu=>allzu; here we've matched just past "all". - // Rather than terminating, attempt to continue in the buffer. - if (j == end) { - int max = s.length; - if (end < max && s[end] == ' ') { - // Find next word - for (; end < max; end++) { - byte cb = s[end]; - if (!isLetter(cb)) { - if (cb == ' ' && end == j) { - continue; - } - break; - } - } - } - } - } - - if (j == end) { - break; - } - if (b == '*') { - // Glob match (only supported at the end) - return 0; - } - byte cb = s[j]; - int delta = b - cb; - if (delta != 0) { - cb = toLowerCase(cb); - b = toLowerCase(b); - delta = b - cb; - if (delta != 0) { - return delta; - } - } - - if (b == terminator || cb == terminator) { - return delta; - } - } - - return data[i] - terminator; - } - - /** - * Look up whether this word is a typo, and if so, return the typo itself - * and one or more likely meanings - * - * @param text the string containing the word - * @param begin the index of the first character in the word - * @param end the index of the first character after the word. Note that the - * search may extend <b>beyond</b> this index, if for example the - * word matches a multi-word typo in the dictionary - * @return a list of the typo itself followed by the replacement strings if - * the word represents a typo, and null otherwise - */ - @Nullable - public List<String> getTypos(@NonNull CharSequence text, int begin, int end) { - assert end <= text.length(); - - if (assertionsEnabled()) { - for (int i = begin; i < end; i++) { - char c = text.charAt(i); - if (c >= 128) { - assert false : "Call the UTF-8 version of this method instead"; - return null; - } - } - } - - int low = 0; - int high = mWordCount - 1; - while (low <= high) { - int middle = (low + high) >>> 1; - int offset = mIndices[middle]; - - if (DEBUG_SEARCH) { - System.out.println("Comparing string " + text +" with entry at " + offset - + ": " + dumpEntry(offset)); - } - - // Compare the word at the given index. - int compare = compare(mData, offset, (byte) 0, text, begin, end); - - if (compare == 0) { - offset = mIndices[middle]; - - // Don't allow matching uncapitalized words, such as "enlish", when - // the dictionary word is capitalized, "Enlish". - if (mData[offset] != text.charAt(begin) - && Character.isLowerCase(text.charAt(begin))) { - return null; - } - - // Make sure there is a case match; we only want to allow - // matching capitalized words to capitalized typos or uncapitalized typos - // (e.g. "Teh" and "teh" to "the"), but not uncapitalized words to capitalized - // typos (e.g. "enlish" to "Enlish"). - String glob = null; - for (int i = begin; ; i++) { - byte b = mData[offset++]; - if (b == 0) { - offset--; - break; - } else if (b == '*') { - int globEnd = i; - while (globEnd < text.length() - && Character.isLetter(text.charAt(globEnd))) { - globEnd++; - } - glob = text.subSequence(i, globEnd).toString(); - break; - } - char c = text.charAt(i); - byte cb = (byte) c; - if (b != cb && i > begin) { - return null; - } - } - - return computeSuggestions(mIndices[middle], offset, glob); - } - - if (compare < 0) { - low = middle + 1; - } else if (compare > 0) { - high = middle - 1; - } else { - assert false; // compare == 0 already handled above - return null; - } - } - - return null; - } - - /** - * Look up whether this word is a typo, and if so, return the typo itself - * and one or more likely meanings - * - * @param utf8Text the string containing the word, encoded as UTF-8 - * @param begin the index of the first character in the word - * @param end the index of the first character after the word. Note that the - * search may extend <b>beyond</b> this index, if for example the - * word matches a multi-word typo in the dictionary - * @return a list of the typo itself followed by the replacement strings if - * the word represents a typo, and null otherwise - */ - @Nullable - public List<String> getTypos(@NonNull byte[] utf8Text, int begin, int end) { - assert end <= utf8Text.length; - - int low = 0; - int high = mWordCount - 1; - while (low <= high) { - int middle = (low + high) >>> 1; - int offset = mIndices[middle]; - - if (DEBUG_SEARCH) { - String s = new String(Arrays.copyOfRange(utf8Text, begin, end), Charsets.UTF_8); - System.out.println("Comparing string " + s +" with entry at " + offset - + ": " + dumpEntry(offset)); - System.out.println(" middle=" + middle + ", low=" + low + ", high=" + high); - } - - // Compare the word at the given index. - int compare = compare(mData, offset, (byte) 0, utf8Text, begin, end); - - if (DEBUG_SEARCH) { - System.out.println(" signum=" + (int)Math.signum(compare) + ", delta=" + compare); - } - - if (compare == 0) { - offset = mIndices[middle]; - - // Don't allow matching uncapitalized words, such as "enlish", when - // the dictionary word is capitalized, "Enlish". - if (mData[offset] != utf8Text[begin] && isUpperCase(mData[offset])) { - return null; - } - - // Make sure there is a case match; we only want to allow - // matching capitalized words to capitalized typos or uncapitalized typos - // (e.g. "Teh" and "teh" to "the"), but not uncapitalized words to capitalized - // typos (e.g. "enlish" to "Enlish"). - String glob = null; - for (int i = begin; ; i++) { - byte b = mData[offset++]; - if (b == 0) { - offset--; - break; - } else if (b == '*') { - int globEnd = i; - while (globEnd < utf8Text.length && isLetter(utf8Text[globEnd])) { - globEnd++; - } - glob = new String(utf8Text, i, globEnd - i, Charsets.UTF_8); - break; - } - byte cb = utf8Text[i]; - if (b != cb && i > begin) { - return null; - } - } - - return computeSuggestions(mIndices[middle], offset, glob); - } - - if (compare < 0) { - low = middle + 1; - } else if (compare > 0) { - high = middle - 1; - } else { - assert false; // compare == 0 already handled above - return null; - } - } - - return null; - } - - private List<String> computeSuggestions(int begin, int offset, String glob) { - String typo = new String(mData, begin, offset - begin, Charsets.UTF_8); - - if (glob != null) { - typo = typo.replaceAll("\\*", glob); //$NON-NLS-1$ - } - - assert mData[offset] == 0; - offset++; - int replacementEnd = offset; - while (mData[replacementEnd] != 0) { - replacementEnd++; - } - String replacements = new String(mData, offset, replacementEnd - offset, Charsets.UTF_8); - List<String> words = new ArrayList<String>(); - words.add(typo); - - // The first entry should be the typo itself. We need to pass this back since due - // to multi-match words and globbing it could extend beyond the initial word range - - for (String s : Splitter.on(',').omitEmptyStrings().trimResults().split(replacements)) { - if (glob != null) { - // Need to append the glob string to each result - words.add(s.replaceAll("\\*", glob)); //$NON-NLS-1$ - } else { - words.add(s); - } - } - - return words; - } - - // "Character" handling for bytes. This assumes that the bytes correspond to Unicode - // characters in the ISO 8859-1 range, which is are encoded the same way in UTF-8. - // This obviously won't work to for example uppercase to lowercase conversions for - // multi byte characters, which means we simply won't catch typos if the dictionaries - // contain these. None of the currently included dictionaries do. However, it does - // help us properly deal with punctuation and spacing characters. - - static boolean isUpperCase(byte b) { - return Character.isUpperCase((char) b); - } - - static byte toLowerCase(byte b) { - return (byte) Character.toLowerCase((char) b); - } - - static boolean isSpace(byte b) { - return Character.isWhitespace((char) b); - } - - static boolean isLetter(byte b) { - // Assume that multi byte characters represent letters in other languages. - // Obviously, it could be unusual punctuation etc but letters are more likely - // in this context. - return Character.isLetter((char) b) || (b & 0x80) != 0; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypographyDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypographyDetector.java deleted file mode 100644 index ae5f24c..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypographyDetector.java +++ /dev/null @@ -1,529 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.TAG_STRING; -import static com.android.SdkConstants.TAG_STRING_ARRAY; - -import com.android.annotations.NonNull; -import com.android.annotations.VisibleForTesting; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Checks for various typographical issues in string definitions. - */ -public class TypographyDetector extends ResourceXmlDetector { - /** Replace hyphens with dashes? */ - public static final Issue DASHES = Issue.create( - "TypographyDashes", //$NON-NLS-1$ - "Looks for usages of hyphens which can be replaced by n dash and m dash characters", - "The \"n dash\" (\u2013, –) and the \"m dash\" (\u2014, —) " + - "characters are used for ranges (n dash) and breaks (m dash). Using these " + - "instead of plain hyphens can make text easier to read and your application " + - "will look more polished.", - Category.TYPOGRAPHY, - 5, - Severity.WARNING, - TypographyDetector.class, - Scope.RESOURCE_FILE_SCOPE). - setMoreInfo("http://en.wikipedia.org/wiki/Dash"); //$NON-NLS-1$ - - /** Replace dumb quotes with smart quotes? */ - public static final Issue QUOTES = Issue.create( - "TypographyQuotes", //$NON-NLS-1$ - "Looks for straight quotes which can be replaced by curvy quotes", - "Straight single quotes and double quotes, when used as a pair, can be replaced " + - "by \"curvy quotes\" (or directional quotes). This can make the text more " + - "readable.\n" + - "\n" + - "Note that you should never use grave accents and apostrophes to quote, " + - "`like this'.\n" + - "\n" + - "(Also note that you should not use curvy quotes for code fragments.)", - Category.TYPOGRAPHY, - 5, - Severity.WARNING, - TypographyDetector.class, - Scope.RESOURCE_FILE_SCOPE). - setMoreInfo("http://en.wikipedia.org/wiki/Quotation_mark"). //$NON-NLS-1$ - // This feature is apparently controversial: recent apps have started using - // straight quotes to avoid inconsistencies. Disabled by default for now. - setEnabledByDefault(false); - - /** Replace fraction strings with fraction characters? */ - public static final Issue FRACTIONS = Issue.create( - "TypographyFractions", //$NON-NLS-1$ - "Looks for fraction strings which can be replaced with a fraction character", - "You can replace certain strings, such as 1/2, and 1/4, with dedicated " + - "characters for these, such as \u00BD (½) and \00BC (¼). " + - "This can help make the text more readable.", - Category.TYPOGRAPHY, - 5, - Severity.WARNING, - TypographyDetector.class, - Scope.RESOURCE_FILE_SCOPE). - setMoreInfo("http://en.wikipedia.org/wiki/Number_Forms"); //$NON-NLS-1$ - - /** Replace ... with the ellipsis character? */ - public static final Issue ELLIPSIS = Issue.create( - "TypographyEllipsis", //$NON-NLS-1$ - "Looks for ellipsis strings (...) which can be replaced with an ellipsis character", - "You can replace the string \"...\" with a dedicated ellipsis character, " + - "ellipsis character (\u2026, …). This can help make the text more readable.", - Category.TYPOGRAPHY, - 5, - Severity.WARNING, - TypographyDetector.class, - Scope.RESOURCE_FILE_SCOPE). - setMoreInfo("http://en.wikipedia.org/wiki/Ellipsis"); //$NON-NLS-1$ - - /** The main issue discovered by this detector */ - public static final Issue OTHER = Issue.create( - "TypographyOther", //$NON-NLS-1$ - "Looks for miscellaneous typographical problems like replacing (c) with \u00A9", - "This check looks for miscellaneous typographical problems and offers replacement " + - "sequences that will make the text easier to read and your application more " + - "polished.", - Category.TYPOGRAPHY, - 3, - Severity.WARNING, - TypographyDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - private static final String GRAVE_QUOTE_MESSAGE = - "Avoid quoting with grave accents; use apostrophes or better yet directional quotes instead"; - private static final String ELLIPSIS_MESSAGE = - "Replace \"...\" with ellipsis character (\u2026, …) ?"; - private static final String EN_DASH_MESSAGE = - "Replace \"-\" with an \"en dash\" character (\u2013, –) ?"; - private static final String EM_DASH_MESSAGE = - "Replace \"--\" with an \"em dash\" character (\u2014, —) ?"; - private static final String TYPOGRAPHIC_APOSTROPHE_MESSAGE = - "Replace apostrophe (') with typographic apostrophe (\u2019, ’) ?"; - private static final String SINGLE_QUOTE_MESSAGE = - "Replace straight quotes ('') with directional quotes (\u2018\u2019, ‘ and ’) ?"; - private static final String DBL_QUOTES_MESSAGE = - "Replace straight quotes (\") with directional quotes (\u201C\u201D, “ and ”) ?"; - private static final String COPYRIGHT_MESSAGE = - "Replace (c) with copyright symbol \u00A9 (©) ?"; - - /** - * Pattern used to detect scenarios which can be replaced with n dashes: a - * numeric range with a hyphen in the middle (and possibly spaces) - */ - @VisibleForTesting - static final Pattern HYPHEN_RANGE_PATTERN = - Pattern.compile(".*(\\d+\\s*)-(\\s*\\d+).*"); //$NON-NLS-1$ - - /** - * Pattern used to detect scenarios where a grave accent mark is used - * to do ASCII quotations of the form `this'' or ``this'', which is frowned upon. - * This pattern tries to avoid falsely complaining about strings like - * "Type Option-` then 'Escape'." - */ - @VisibleForTesting - static final Pattern GRAVE_QUOTATION = - Pattern.compile("(^[^`]*`[^'`]+'[^']*$)|(^[^`]*``[^'`]+''[^']*$)"); //$NON-NLS-1$ - - /** - * Pattern used to detect common fractions, e.g. 1/2, 1/3, 2/3, 1/4, 3/4 and - * variations like 2 / 3, but not 11/22 and so on. - */ - @VisibleForTesting - static final Pattern FRACTION_PATTERN = - Pattern.compile(".*\\b([13])\\s*/\\s*([234])\\b.*"); //$NON-NLS-1$ - - /** - * Pattern used to detect single quote strings, such as 'hello', but - * not just quoted strings like 'Double quote: "', and not sentences - * where there are multiple apostrophes but not in a quoting context such - * as "Mind Your P's and Q's". - */ - @VisibleForTesting - static final Pattern SINGLE_QUOTE = - Pattern.compile(".*\\W*'[^']+'(\\W.*)?"); //$NON-NLS-1$ - - private static final String FRACTION_MESSAGE = - "Use fraction character %1$c (%2$s) instead of %3$s ?"; - - private static final String FRACTION_MESSAGE_PATTERN = - "Use fraction character (.+) \\((.+)\\) instead of (.+) \\?"; - - private boolean mCheckDashes; - private boolean mCheckQuotes; - private boolean mCheckFractions; - private boolean mCheckEllipsis; - private boolean mCheckMisc; - - /** Constructs a new {@link TypographyDetector} */ - public TypographyDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.VALUES; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - TAG_STRING, - TAG_STRING_ARRAY - ); - } - - @Override - public void beforeCheckProject(@NonNull Context context) { - mCheckDashes = context.isEnabled(DASHES); - mCheckQuotes = context.isEnabled(QUOTES); - mCheckFractions = context.isEnabled(FRACTIONS); - mCheckEllipsis = context.isEnabled(ELLIPSIS); - mCheckMisc = context.isEnabled(OTHER); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - NodeList childNodes = element.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.TEXT_NODE) { - String text = child.getNodeValue(); - checkText(context, element, child, text); - } else if (child.getNodeType() == Node.ELEMENT_NODE && - child.getParentNode().getNodeName().equals(TAG_STRING_ARRAY)) { - // String array item children - NodeList items = child.getChildNodes(); - for (int j = 0, m = items.getLength(); j < m; j++) { - Node item = items.item(j); - if (item.getNodeType() == Node.TEXT_NODE) { - String text = item.getNodeValue(); - checkText(context, child, item, text); - } - } - } - } - } - - private void checkText(XmlContext context, Node element, Node textNode, String text) { - if (mCheckEllipsis) { - // Replace ... with ellipsis character? - int ellipsis = text.indexOf("..."); //$NON-NLS-1$ - if (ellipsis != -1 && !text.startsWith(".", ellipsis + 3)) { //$NON-NLS-1$ - context.report(ELLIPSIS, element, context.getLocation(textNode), - ELLIPSIS_MESSAGE, null); - } - } - - // Dashes - if (mCheckDashes) { - int hyphen = text.indexOf('-'); - if (hyphen != -1) { - // n dash - Matcher matcher = HYPHEN_RANGE_PATTERN.matcher(text); - if (matcher.matches()) { - // Make sure that if there is no space before digit there isn't - // one on the left either -- since we don't want to consider - // "1 2 -3" as a range from 2 to 3 - boolean isNegativeNumber = - !Character.isWhitespace(matcher.group(2).charAt(0)) && - Character.isWhitespace(matcher.group(1).charAt( - matcher.group(1).length() - 1)); - if (!isNegativeNumber && !isAnalyticsTrackingId((Element) element)) { - context.report(DASHES, element, context.getLocation(textNode), - EN_DASH_MESSAGE, - null); - } - } - - // m dash - int emdash = text.indexOf("--"); //$NON-NLS-1$ - // Don't suggest replacing -- or "--" with an m dash since these are sometimes - // used as digit marker strings - if (emdash > 1 && !text.startsWith("-", emdash + 2)) { //$NON-NLS-1$ - context.report(DASHES, element, context.getLocation(textNode), - EM_DASH_MESSAGE, null); - } - } - } - - if (mCheckQuotes) { - // Check for single quotes that can be replaced with directional quotes - int quoteStart = text.indexOf('\''); - if (quoteStart != -1) { - int quoteEnd = text.indexOf('\'', quoteStart + 1); - if (quoteEnd != -1 && quoteEnd > quoteStart + 1 - && (quoteEnd < text.length() -1 || quoteStart > 0) - && SINGLE_QUOTE.matcher(text).matches()) { - context.report(QUOTES, element, context.getLocation(textNode), - SINGLE_QUOTE_MESSAGE, null); - return; - } - - // Check for apostrophes that can be replaced by typographic apostrophes - if (quoteEnd == -1 && quoteStart > 0 - && Character.isLetterOrDigit(text.charAt(quoteStart - 1))) { - context.report(QUOTES, element, context.getLocation(textNode), - TYPOGRAPHIC_APOSTROPHE_MESSAGE, null); - return; - } - } - - // Check for double quotes that can be replaced by directional double quotes - quoteStart = text.indexOf('"'); - if (quoteStart != -1) { - int quoteEnd = text.indexOf('"', quoteStart + 1); - if (quoteEnd != -1 && quoteEnd > quoteStart + 1) { - if (quoteEnd < text.length() -1 || quoteStart > 0) { - context.report(QUOTES, element, context.getLocation(textNode), - DBL_QUOTES_MESSAGE, null); - return; - } - } - } - - // Check for grave accent quotations - if (text.indexOf('`') != -1 && GRAVE_QUOTATION.matcher(text).matches()) { - // Are we indenting ``like this'' or `this' ? If so, complain - context.report(QUOTES, element, context.getLocation(textNode), - GRAVE_QUOTE_MESSAGE, null); - return; - } - - // Consider suggesting other types of directional quotes, such as guillemets, in - // other languages? - // There are a lot of exceptions and special cases to be considered so - // this will need careful implementation and testing. - // See http://en.wikipedia.org/wiki/Non-English_usage_of_quotation_marks - } - - // Fraction symbols? - if (mCheckFractions && text.indexOf('/') != -1) { - Matcher matcher = FRACTION_PATTERN.matcher(text); - if (matcher.matches()) { - String top = matcher.group(1); // Numerator - String bottom = matcher.group(2); // Denominator - if (top.equals("1") && bottom.equals("2")) { //$NON-NLS-1$ //$NON-NLS-2$ - context.report(FRACTIONS, element, context.getLocation(textNode), - String.format(FRACTION_MESSAGE, '\u00BD', "½", "1/2"), null); - } else if (top.equals("1") && bottom.equals("4")) { //$NON-NLS-1$ //$NON-NLS-2$ - context.report(FRACTIONS, element, context.getLocation(textNode), - String.format(FRACTION_MESSAGE, '\u00BC', "¼", "1/4"), null); - } else if (top.equals("3") && bottom.equals("4")) { //$NON-NLS-1$ //$NON-NLS-2$ - context.report(FRACTIONS, element, context.getLocation(textNode), - String.format(FRACTION_MESSAGE, '\u00BE', "¾", "3/4"), null); - } else if (top.equals("1") && bottom.equals("3")) { //$NON-NLS-1$ //$NON-NLS-2$ - context.report(FRACTIONS, element, context.getLocation(textNode), - String.format(FRACTION_MESSAGE, '\u2153', "⅓", "1/3"), null); - } else if (top.equals("2") && bottom.equals("3")) { //$NON-NLS-1$ //$NON-NLS-2$ - context.report(FRACTIONS, element, context.getLocation(textNode), - String.format(FRACTION_MESSAGE, '\u2154', "⅔", "2/3"), null); - } - } - } - - if (mCheckMisc) { - // Fix copyright symbol? - if (text.indexOf('(') != -1 - && (text.contains("(c)") || text.contains("(C)"))) { //$NON-NLS-1$ //$NON-NLS-2$ - // Suggest replacing with copyright symbol? - context.report(OTHER, element, context.getLocation(textNode), - COPYRIGHT_MESSAGE, null); - // Replace (R) and TM as well? There are unicode characters for these but they - // are probably not very common within Android app strings. - } - } - } - - private static boolean isAnalyticsTrackingId(Element element) { - String name = element.getAttribute(ATTR_NAME); - return "ga_trackingId".equals(name); //$NON-NLS-1$ - } - - /** - * An object describing a single edit to be made. The offset points to a - * location to start editing; the length is the number of characters to - * delete, and the replaceWith string points to a string to insert at the - * offset. Note that this can model not just replacement edits but deletions - * (empty replaceWith) and insertions (replace length = 0) too. - */ - public static class ReplaceEdit { - /** The offset of the edit */ - public final int offset; - /** The number of characters to delete at the offset */ - public final int length; - /** The characters to insert at the offset */ - public final String replaceWith; - - /** - * Creates a new replace edit - * - * @param offset the offset of the edit - * @param length the number of characters to delete at the offset - * @param replaceWith the characters to insert at the offset - */ - public ReplaceEdit(int offset, int length, String replaceWith) { - super(); - this.offset = offset; - this.length = length; - this.replaceWith = replaceWith; - } - } - - /** - * Returns a list of edits to be applied to fix the suggestion made by the - * given warning. The specific issue id and message should be the message - * provided by this detector in an earlier run. - * <p> - * This is intended to help tools implement automatic fixes of these - * warnings. The reason only the message and issue id can be provided - * instead of actual state passed in the data field to a reporter is that - * fix operation can be run much later than the lint is processed (for - * example, in a subsequent run of the IDE when only the warnings have been - * persisted), - * - * @param issueId the issue id, which should be the id for one of the - * typography issues - * @param message the actual error message, which should be a message - * provided by this detector - * @param textNode a text node which corresponds to the text node the - * warning operated on - * @return a list of edits, which is never null but could be empty. The - * offsets in the edit objects are relative to the text node. - */ - public static List<ReplaceEdit> getEdits(String issueId, String message, Node textNode) { - return getEdits(issueId, message, textNode.getNodeValue()); - } - - /** - * Returns a list of edits to be applied to fix the suggestion made by the - * given warning. The specific issue id and message should be the message - * provided by this detector in an earlier run. - * <p> - * This is intended to help tools implement automatic fixes of these - * warnings. The reason only the message and issue id can be provided - * instead of actual state passed in the data field to a reporter is that - * fix operation can be run much later than the lint is processed (for - * example, in a subsequent run of the IDE when only the warnings have been - * persisted), - * - * @param issueId the issue id, which should be the id for one of the - * typography issues - * @param message the actual error message, which should be a message - * provided by this detector - * @param text the text of the XML node where the warning appeared - * @return a list of edits, which is never null but could be empty. The - * offsets in the edit objects are relative to the text node. - */ - public static List<ReplaceEdit> getEdits(String issueId, String message, String text) { - List<ReplaceEdit> edits = new ArrayList<ReplaceEdit>(); - if (message.equals(ELLIPSIS_MESSAGE)) { - int offset = text.indexOf("..."); //$NON-NLS-1$ - if (offset != -1) { - edits.add(new ReplaceEdit(offset, 3, "\u2026")); //$NON-NLS-1$ - } - } else if (message.equals(EN_DASH_MESSAGE)) { - int offset = text.indexOf('-'); - if (offset != -1) { - edits.add(new ReplaceEdit(offset, 1, "\u2013")); //$NON-NLS-1$ - } - } else if (message.equals(EM_DASH_MESSAGE)) { - int offset = text.indexOf("--"); //$NON-NLS-1$ - if (offset != -1) { - edits.add(new ReplaceEdit(offset, 2, "\u2014")); //$NON-NLS-1$ - } - } else if (message.equals(TYPOGRAPHIC_APOSTROPHE_MESSAGE)) { - int offset = text.indexOf('\''); - if (offset != -1) { - edits.add(new ReplaceEdit(offset, 1, "\u2019")); //$NON-NLS-1$ - } - } else if (message.equals(COPYRIGHT_MESSAGE)) { - int offset = text.indexOf("(c)"); //$NON-NLS-1$ - if (offset == -1) { - offset = text.indexOf("(C)"); //$NON-NLS-1$ - } - if (offset != -1) { - edits.add(new ReplaceEdit(offset, 3, "\u00A9")); //$NON-NLS-1$ - } - } else if (message.equals(SINGLE_QUOTE_MESSAGE)) { - int offset = text.indexOf('\''); - if (offset != -1) { - int endOffset = text.indexOf('\'', offset + 1); //$NON-NLS-1$ - if (endOffset != -1) { - edits.add(new ReplaceEdit(offset, 1, "\u2018")); //$NON-NLS-1$ - edits.add(new ReplaceEdit(endOffset, 1, "\u2019")); //$NON-NLS-1$ - } - } - } else if (message.equals(DBL_QUOTES_MESSAGE)) { - int offset = text.indexOf('"'); - if (offset != -1) { - int endOffset = text.indexOf('"', offset + 1); - if (endOffset != -1) { - edits.add(new ReplaceEdit(offset, 1, "\u201C")); //$NON-NLS-1$ - edits.add(new ReplaceEdit(endOffset, 1, "\u201D")); //$NON-NLS-1$ - } - } - } else if (message.equals(GRAVE_QUOTE_MESSAGE)) { - int offset = text.indexOf('`'); - if (offset != -1) { - int endOffset = text.indexOf('\'', offset + 1); - if (endOffset != -1) { - edits.add(new ReplaceEdit(offset, 1, "\u2018")); //$NON-NLS-1$ - edits.add(new ReplaceEdit(endOffset, 1, "\u2019")); //$NON-NLS-1$ - } - } - } else { - Matcher matcher = Pattern.compile(FRACTION_MESSAGE_PATTERN).matcher(message); - if (matcher.find()) { - // "Use fraction character %1$c (%2$s) instead of %3$s ?"; - String replace = matcher.group(3); - int offset = text.indexOf(replace); - if (offset != -1) { - String replaceWith = matcher.group(2); - edits.add(new ReplaceEdit(offset, replace.length(), replaceWith)); - } - } - } - - return edits; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/UnusedResourceDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/UnusedResourceDetector.java deleted file mode 100644 index caa112a..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/UnusedResourceDetector.java +++ /dev/null @@ -1,594 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_REF_PREFIX; -import static com.android.SdkConstants.DOT_GIF; -import static com.android.SdkConstants.DOT_JPG; -import static com.android.SdkConstants.DOT_PNG; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.RESOURCE_CLR_STYLEABLE; -import static com.android.SdkConstants.RESOURCE_CLZ_ARRAY; -import static com.android.SdkConstants.RESOURCE_CLZ_ID; -import static com.android.SdkConstants.R_ATTR_PREFIX; -import static com.android.SdkConstants.R_CLASS; -import static com.android.SdkConstants.R_ID_PREFIX; -import static com.android.SdkConstants.R_PREFIX; -import static com.android.SdkConstants.TAG_ARRAY; -import static com.android.SdkConstants.TAG_ITEM; -import static com.android.SdkConstants.TAG_PLURALS; -import static com.android.SdkConstants.TAG_RESOURCES; -import static com.android.SdkConstants.TAG_STRING_ARRAY; -import static com.android.SdkConstants.TAG_STYLE; -import static com.android.tools.lint.detector.api.LintUtils.endsWith; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Project; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 com.google.common.collect.Lists; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import lombok.ast.AstVisitor; -import lombok.ast.ClassDeclaration; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.NormalTypeBody; -import lombok.ast.VariableDeclaration; -import lombok.ast.VariableDefinition; - -/** - * Finds unused resources. - * <p> - * Note: This detector currently performs *string* analysis to check Java files. - * The Lint API needs an official Java AST API (or map to an existing one like - * BCEL for bytecode analysis etc) and once it does this should be updated to - * use it. - */ -public class UnusedResourceDetector extends ResourceXmlDetector implements Detector.JavaScanner { - - /** Unused resources (other than ids). */ - public static final Issue ISSUE = Issue.create("UnusedResources", //$NON-NLS-1$ - "Looks for unused resources", - "Unused resources make applications larger and slow down builds.", - Category.PERFORMANCE, - 3, - Severity.WARNING, - UnusedResourceDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.ALL_RESOURCE_FILES, Scope.ALL_JAVA_FILES)); - - /** Unused id's */ - public static final Issue ISSUE_IDS = Issue.create("UnusedIds", //$NON-NLS-1$ - "Looks for unused id's", - "This resource id definition appears not to be needed since it is not referenced " + - "from anywhere. Having id definitions, even if unused, is not necessarily a bad " + - "idea since they make working on layouts and menus easier, so there is not a " + - "strong reason to delete these.", - Category.PERFORMANCE, - 1, - Severity.WARNING, - UnusedResourceDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.ALL_RESOURCE_FILES, Scope.ALL_JAVA_FILES)) - .setEnabledByDefault(false); - - private Set<String> mDeclarations; - private Set<String> mReferences; - private Map<String, Location> mUnused; - - /** - * Constructs a new {@link UnusedResourceDetector} - */ - public UnusedResourceDetector() { - } - - @Override - public void run(@NonNull Context context) { - assert false; - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - return true; - } - - @Override - public void beforeCheckProject(@NonNull Context context) { - if (context.getPhase() == 1) { - mDeclarations = new HashSet<String>(300); - mReferences = new HashSet<String>(300); - } - } - - // ---- Implements JavaScanner ---- - - @Override - public void beforeCheckFile(@NonNull Context context) { - File file = context.file; - - String fileName = file.getName(); - boolean isXmlFile = endsWith(fileName, DOT_XML); - if (isXmlFile - || endsWith(fileName, DOT_PNG) - || endsWith(fileName, DOT_JPG) - || endsWith(fileName, DOT_GIF)) { - String parentName = file.getParentFile().getName(); - int dash = parentName.indexOf('-'); - String typeName = parentName.substring(0, dash == -1 ? parentName.length() : dash); - ResourceType type = ResourceType.getEnum(typeName); - if (type != null && LintUtils.isFileBasedResourceType(type)) { - String baseName = fileName.substring(0, fileName.length() - DOT_XML.length()); - String resource = R_PREFIX + typeName + '.' + baseName; - if (context.getPhase() == 1) { - mDeclarations.add(resource); - } else { - assert context.getPhase() == 2; - if (mUnused.containsKey(resource)) { - // Check whether this is an XML document that has a tools:ignore attribute - // on the document element: if so don't record it as a declaration. - if (isXmlFile && context instanceof XmlContext) { - XmlContext xmlContext = (XmlContext) context; - if (xmlContext.document != null - && xmlContext.document.getDocumentElement() != null) { - Element root = xmlContext.document.getDocumentElement(); - if (xmlContext.getDriver().isSuppressed(ISSUE, root)) { - // Also remove it from consideration such that even the - // presence of this field in the R file is ignored. - mUnused.remove(resource); - return; - } - } - } - - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - mUnused.remove(resource); - return; - } - - recordLocation(resource, Location.create(file)); - } - } - } - } - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (context.getPhase() == 1) { - mDeclarations.removeAll(mReferences); - Set<String> unused = mDeclarations; - mReferences = null; - mDeclarations = null; - - // Remove styles and attributes: they may be used, analysis isn't complete for these - List<String> styles = new ArrayList<String>(); - for (String resource : unused) { - // R.style.x, R.styleable.x, R.attr - if (resource.startsWith("R.style") //$NON-NLS-1$ - || resource.startsWith("R.attr")) { //$NON-NLS-1$ - styles.add(resource); - } - } - unused.removeAll(styles); - - // Remove id's if the user has disabled reporting issue ids - if (!unused.isEmpty() && !context.isEnabled(ISSUE_IDS)) { - // Remove all R.id references - List<String> ids = new ArrayList<String>(); - for (String resource : unused) { - if (resource.startsWith(R_ID_PREFIX)) { - ids.add(resource); - } - } - unused.removeAll(ids); - } - - if (!unused.isEmpty() && !context.getDriver().hasParserErrors()) { - mUnused = new HashMap<String, Location>(unused.size()); - for (String resource : unused) { - mUnused.put(resource, null); - } - - // Request another pass, and in the second pass we'll gather location - // information for all declaration locations we've found - context.requestRepeat(this, Scope.ALL_RESOURCES_SCOPE); - } - } else { - assert context.getPhase() == 2; - - // Report any resources that we (for some reason) could not find a declaration - // location for - if (!mUnused.isEmpty()) { - // Fill in locations for files that we didn't encounter in other ways - for (Map.Entry<String, Location> entry : mUnused.entrySet()) { - String resource = entry.getKey(); - Location location = entry.getValue(); - if (location != null) { - continue; - } - - // Try to figure out the file if it's a file based resource (such as R.layout) -- - // in that case we can figure out the filename since it has a simple mapping - // from the resource name (though the presence of qualifiers like -land etc - // makes it a little tricky if there's no base file provided) - int secondDot = resource.indexOf('.', 2); - String typeName = resource.substring(2, secondDot); // 2: Skip R. - ResourceType type = ResourceType.getEnum(typeName); - if (type != null && LintUtils.isFileBasedResourceType(type)) { - String name = resource.substring(secondDot + 1); - - List<File> folders = Lists.newArrayList(); - List<File> resourceFolders = context.getProject().getResourceFolders(); - for (File res : resourceFolders) { - File[] f = res.listFiles(); - if (f != null) { - folders.addAll(Arrays.asList(f)); - } - } - if (folders != null) { - // Process folders in alphabetical order such that we process - // based folders first: we want the locations in base folder - // order - Collections.sort(folders, new Comparator<File>() { - @Override - public int compare(File file1, File file2) { - return file1.getName().compareTo(file2.getName()); - } - }); - for (File folder : folders) { - if (folder.getName().startsWith(typeName)) { - File[] files = folder.listFiles(); - if (files != null) { - for (File file : files) { - String fileName = file.getName(); - if (fileName.startsWith(name) - && fileName.startsWith(".", //$NON-NLS-1$ - name.length())) { - recordLocation(resource, Location.create(file)); - } - } - } - } - } - } - } - } - - List<String> sorted = new ArrayList<String>(mUnused.keySet()); - Collections.sort(sorted); - - Boolean skippedLibraries = null; - - for (String resource : sorted) { - Location location = mUnused.get(resource); - if (location != null) { - // We were prepending locations, but we want to prefer the base folders - location = Location.reverse(location); - } - - if (location == null) { - if (skippedLibraries == null) { - skippedLibraries = false; - for (Project project : context.getDriver().getProjects()) { - if (!project.getReportIssues()) { - skippedLibraries = true; - break; - } - } - } - if (skippedLibraries) { - // Skip this resource if we don't have a location, and one or - // more library projects were skipped; the resource was very - // probably defined in that library project and only encountered - // in the main project's java R file - continue; - } - } - - String message = String.format("The resource %1$s appears to be unused", - resource); - Issue issue = getIssue(resource); - // TODO: Compute applicable node scope - context.report(issue, location, message, resource); - } - } - } - } - - private static Issue getIssue(String resource) { - return resource.startsWith(R_ID_PREFIX) ? ISSUE_IDS : ISSUE; - } - - private void recordLocation(String resource, Location location) { - Location oldLocation = mUnused.get(resource); - if (oldLocation != null) { - location.setSecondary(oldLocation); - } - mUnused.put(resource, location); - } - - @Override - public Collection<String> getApplicableAttributes() { - return ALL; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - TAG_STYLE, - TAG_RESOURCES, - TAG_ARRAY, - TAG_STRING_ARRAY, - TAG_PLURALS - ); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (TAG_RESOURCES.equals(element.getTagName())) { - for (Element item : LintUtils.getChildren(element)) { - Attr nameAttribute = item.getAttributeNode(ATTR_NAME); - if (nameAttribute != null) { - String name = nameAttribute.getValue(); - if (name.indexOf('.') != -1) { - name = name.replace('.', '_'); - } - String type = item.getTagName(); - if (type.equals(TAG_ITEM)) { - type = RESOURCE_CLZ_ID; - } else if (type.equals("declare-styleable")) { //$NON-NLS-1$ - type = RESOURCE_CLR_STYLEABLE; - } else if (type.contains("array")) { //$NON-NLS-1$ - // <string-array> etc - type = RESOURCE_CLZ_ARRAY; - } - String resource = R_PREFIX + type + '.' + name; - - if (context.getPhase() == 1) { - mDeclarations.add(resource); - checkChildRefs(item); - } else { - assert context.getPhase() == 2; - if (mUnused.containsKey(resource)) { - if (context.getDriver().isSuppressed(getIssue(resource), item)) { - mUnused.remove(resource); - continue; - } - if (!context.getProject().getReportIssues()) { - mUnused.remove(resource); - continue; - } - if (isAnalyticsFile(context)) { - mUnused.remove(resource); - continue; - } - - recordLocation(resource, context.getLocation(nameAttribute)); - } - } - } - } - } else if (mReferences != null) { - assert TAG_STYLE.equals(element.getTagName()) - || TAG_ARRAY.equals(element.getTagName()) - || TAG_PLURALS.equals(element.getTagName()) - || TAG_STRING_ARRAY.equals(element.getTagName()); - for (Element item : LintUtils.getChildren(element)) { - checkChildRefs(item); - } - } - } - - private static final String ANALYTICS_FILE = "analytics.xml"; //$NON-NLS-1$ - - /** - * Returns true if this XML file corresponds to an Analytics configuration file; - * these contain some attributes read by the library which won't be flagged as - * used by the application - * - * @param context the context used for scanning - * @return true if the file represents an analytics file - */ - public static boolean isAnalyticsFile(Context context) { - File file = context.file; - return file.getPath().endsWith(ANALYTICS_FILE) && file.getName().equals(ANALYTICS_FILE); - } - - private void checkChildRefs(Element item) { - // Look for ?attr/ and @dimen/foo etc references in the item children - NodeList childNodes = item.getChildNodes(); - for (int i = 0, n = childNodes.getLength(); i < n; i++) { - Node child = childNodes.item(i); - if (child.getNodeType() == Node.TEXT_NODE) { - String text = child.getNodeValue(); - - int index = text.indexOf(ATTR_REF_PREFIX); - if (index != -1) { - String name = text.substring(index + ATTR_REF_PREFIX.length()).trim(); - mReferences.add(R_ATTR_PREFIX + name); - } else { - index = text.indexOf('@'); - if (index != -1 && text.indexOf('/', index) != -1 - && !text.startsWith("@android:", index)) { //$NON-NLS-1$ - // Compute R-string, e.g. @string/foo => R.string.foo - String token = text.substring(index + 1).trim().replace('/', '.'); - String r = R_PREFIX + token; - mReferences.add(r); - } - } - } - } - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - String value = attribute.getValue(); - - if (value.startsWith("@+") && !value.startsWith("@+android")) { //$NON-NLS-1$ //$NON-NLS-2$ - String resource = R_PREFIX + value.substring(2).replace('/', '.'); - // We already have the declarations when we scan the R file, but we're tracking - // these here to get attributes for position info - - if (context.getPhase() == 1) { - mDeclarations.add(resource); - } else if (mUnused.containsKey(resource)) { - if (context.getDriver().isSuppressed(getIssue(resource), attribute)) { - mUnused.remove(resource); - return; - } - if (!context.getProject().getReportIssues()) { - mUnused.remove(resource); - return; - } - recordLocation(resource, context.getLocation(attribute)); - return; - } - } else if (mReferences != null) { - if (value.startsWith("@") //$NON-NLS-1$ - && !value.startsWith("@android:")) { //$NON-NLS-1$ - // Compute R-string, e.g. @string/foo => R.string.foo - String r = R_PREFIX + value.substring(1).replace('/', '.'); - mReferences.add(r); - } else if (value.startsWith(ATTR_REF_PREFIX)) { - mReferences.add(R_ATTR_PREFIX + value.substring(ATTR_REF_PREFIX.length())); - } - } - - if (attribute.getNamespaceURI() != null - && !ANDROID_URI.equals(attribute.getNamespaceURI()) && mReferences != null) { - mReferences.add(R_ATTR_PREFIX + attribute.getLocalName()); - } - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.SLOW; - } - - @Override - public List<Class<? extends lombok.ast.Node>> getApplicableNodeTypes() { - return Collections.<Class<? extends lombok.ast.Node>>singletonList(ClassDeclaration.class); - } - - @Override - public boolean appliesToResourceRefs() { - return true; - } - - @Override - public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull lombok.ast.Node node, @NonNull String type, @NonNull String name, - boolean isFramework) { - if (mReferences != null && !isFramework) { - String reference = R_PREFIX + type + '.' + name; - mReferences.add(reference); - } - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - if (mReferences != null) { - return new UnusedResourceVisitor(); - } else { - // Second pass, computing resource declaration locations: No need to look at Java - return null; - } - } - - // Look for references and declarations - private class UnusedResourceVisitor extends ForwardingAstVisitor { - @Override - public boolean visitClassDeclaration(ClassDeclaration node) { - // Look for declarations of R class fields and store them in - // mDeclarations - String description = node.getDescription(); - if (description.equals(R_CLASS)) { - // This is an R class. We can process this class very deliberately. - // The R class has a very specific AST format: - // ClassDeclaration ("R") - // NormalTypeBody - // ClassDeclaration (e.g. "drawable") - // NormalTypeBody - // VariableDeclaration - // VariableDefinition (e.g. "ic_launcher") - for (lombok.ast.Node body : node.getChildren()) { - if (body instanceof NormalTypeBody) { - for (lombok.ast.Node subclass : body.getChildren()) { - if (subclass instanceof ClassDeclaration) { - String className = ((ClassDeclaration) subclass).getDescription(); - for (lombok.ast.Node innerBody : subclass.getChildren()) { - if (innerBody instanceof NormalTypeBody) { - for (lombok.ast.Node field : innerBody.getChildren()) { - if (field instanceof VariableDeclaration) { - for (lombok.ast.Node child : field.getChildren()) { - if (child instanceof VariableDefinition) { - VariableDefinition def = - (VariableDefinition) child; - String name = def.astVariables().first() - .astName().astValue(); - String resource = R_PREFIX + className - + '.' + name; - mDeclarations.add(resource); - } // Else: It could be a comment node - } - } - } - } - } - } - } - } - } - - return true; - } - - return false; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/UseCompoundDrawableDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/UseCompoundDrawableDetector.java deleted file mode 100644 index db5de4d..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/UseCompoundDrawableDetector.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_BACKGROUND; -import static com.android.SdkConstants.ATTR_LAYOUT_WEIGHT; -import static com.android.SdkConstants.ATTR_SCALE_TYPE; -import static com.android.SdkConstants.IMAGE_VIEW; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.TEXT_VIEW; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -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; -import java.util.Collections; -import java.util.List; - -/** - * Checks whether the current node can be replaced by a TextView using compound - * drawables. - */ -public class UseCompoundDrawableDetector extends LayoutDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "UseCompoundDrawables", //$NON-NLS-1$ - "Checks whether the current node can be replaced by a TextView using compound drawables.", - "A `LinearLayout` which contains an `ImageView` and a `TextView` can be more " + - "efficiently handled as a compound drawable (a single TextView, using the " + - "`drawableTop`, `drawableLeft`, `drawableRight` and/or `drawableBottom` attributes " + - "to draw one or more images adjacent to the text).\n" + - "\n" + - "If the two widgets are offset from each other with " + - "margins, this can be replaced with a `drawablePadding` attribute.\n" + - "\n" + - "There's a lint quickfix to perform this conversion in the Eclipse plugin.", - Category.PERFORMANCE, - 6, - Severity.WARNING, - UseCompoundDrawableDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new {@link UseCompoundDrawableDetector} */ - public UseCompoundDrawableDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Collections.singletonList( - LINEAR_LAYOUT - ); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - int childCount = LintUtils.getChildCount(element); - if (childCount == 2) { - List<Element> children = LintUtils.getChildren(element); - Element first = children.get(0); - Element second = children.get(1); - if ((first.getTagName().equals(IMAGE_VIEW) && - second.getTagName().equals(TEXT_VIEW) && - !first.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT)) || - ((second.getTagName().equals(IMAGE_VIEW) && - first.getTagName().equals(TEXT_VIEW) && - !second.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT)))) { - // If the layout has a background, ignore since it would disappear from - // the TextView - if (element.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND)) { - return; - } - - // Certain scale types cannot be done with compound drawables - String scaleType = first.getTagName().equals(IMAGE_VIEW) - ? first.getAttributeNS(ANDROID_URI, ATTR_SCALE_TYPE) - : second.getAttributeNS(ANDROID_URI, ATTR_SCALE_TYPE); - if (scaleType != null && !scaleType.isEmpty()) { - // For now, ignore if any scale type is set - return; - } - - context.report(ISSUE, element, context.getLocation(element), - "This tag and its children can be replaced by one <TextView/> and " + - "a compound drawable", null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/UselessViewDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/UselessViewDetector.java deleted file mode 100644 index 867c3c4..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/UselessViewDetector.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ABSOLUTE_LAYOUT; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_BACKGROUND; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_STYLE; -import static com.android.SdkConstants.FRAME_LAYOUT; -import static com.android.SdkConstants.GRID_LAYOUT; -import static com.android.SdkConstants.GRID_VIEW; -import static com.android.SdkConstants.HORIZONTAL_SCROLL_VIEW; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.VIEW_MERGE; -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.TABLE_LAYOUT; -import static com.android.SdkConstants.TABLE_ROW; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -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.android.tools.lint.detector.api.XmlContext; - -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * Checks whether the current node can be removed without affecting the layout. - */ -public class UselessViewDetector extends LayoutDetector { - /** Issue of including a parent that has no value on its own */ - public static final Issue USELESS_PARENT = Issue.create( - "UselessParent", //$NON-NLS-1$ - "Checks whether a parent layout can be removed.", - "A layout with children that has no siblings, is not a scrollview or " + - "a root layout, and does not have a background, can be removed and have " + - "its children moved directly into the parent for a flatter and more " + - "efficient layout hierarchy.", - Category.PERFORMANCE, - 2, - Severity.WARNING, - UselessViewDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Issue of including a leaf that isn't shown */ - public static final Issue USELESS_LEAF = Issue.create( - "UselessLeaf", //$NON-NLS-1$ - "Checks whether a leaf layout can be removed.", - "A layout that has no children or no background can often be removed (since it " + - "is invisible) for a flatter and more efficient layout hierarchy.", - Category.PERFORMANCE, - 2, - Severity.WARNING, - UselessViewDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new {@link UselessViewDetector} */ - public UselessViewDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - private static final List<String> CONTAINERS = new ArrayList<String>(18); - static { - CONTAINERS.add(ABSOLUTE_LAYOUT); - CONTAINERS.add(FRAME_LAYOUT); - CONTAINERS.add(GRID_LAYOUT); - CONTAINERS.add(GRID_VIEW); - CONTAINERS.add(HORIZONTAL_SCROLL_VIEW); - CONTAINERS.add("ImageSwitcher"); //$NON-NLS-1$ - CONTAINERS.add(LINEAR_LAYOUT); - CONTAINERS.add(RADIO_GROUP); - CONTAINERS.add(RELATIVE_LAYOUT); - CONTAINERS.add(SCROLL_VIEW); - CONTAINERS.add("SlidingDrawer"); //$NON-NLS-1$ - CONTAINERS.add("StackView"); //$NON-NLS-1$ - CONTAINERS.add(TABLE_LAYOUT); - CONTAINERS.add(TABLE_ROW); - CONTAINERS.add("TextSwitcher"); //$NON-NLS-1$ - CONTAINERS.add("ViewAnimator"); //$NON-NLS-1$ - CONTAINERS.add("ViewFlipper"); //$NON-NLS-1$ - CONTAINERS.add("ViewSwitcher"); //$NON-NLS-1$ - // Available ViewGroups that are not included by this check: - // CONTAINERS.add("android.gesture.GestureOverlayView"); - // CONTAINERS.add("AdapterViewFlipper"); - // CONTAINERS.add("DialerFilter"); - // CONTAINERS.add("ExpandableListView"); - // CONTAINERS.add("ListView"); - // CONTAINERS.add("MediaController"); - // CONTAINERS.add("merge"); - // CONTAINERS.add("SearchView"); - // CONTAINERS.add("TabWidget"); - // CONTAINERS.add("TabHost"); - } - @Override - public Collection<String> getApplicableElements() { - return CONTAINERS; - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - int childCount = LintUtils.getChildCount(element); - if (childCount == 0) { - // Check to see if this is a leaf layout that can be removed - checkUselessLeaf(context, element); - } else { - // Check to see if this is a middle-man layout which can be removed - checkUselessMiddleLayout(context, element); - } - } - - // This is the old UselessLayoutCheck from layoutopt - private static void checkUselessMiddleLayout(XmlContext context, Element element) { - // Conditions: - // - The node has children - // - The node does not have siblings - // - The node's parent is not a scroll view (horizontal or vertical) - // - The node does not have a background or its parent does not have a - // background or neither the node and its parent have a background - // - The parent is not a <merge/> - - Node parentNode = element.getParentNode(); - if (parentNode.getNodeType() != Node.ELEMENT_NODE) { - // Can't remove root - return; - } - - Element parent = (Element) parentNode; - String parentTag = parent.getTagName(); - if (parentTag.equals(SCROLL_VIEW) || parentTag.equals(HORIZONTAL_SCROLL_VIEW) || - parentTag.equals(VIEW_MERGE)) { - // Can't remove if the parent is a scroll view or a merge - return; - } - - // This method is only called when we've already ensured that it has children - assert LintUtils.getChildCount(element) > 0; - - int parentChildCount = LintUtils.getChildCount(parent); - if (parentChildCount != 1) { - // Don't remove if the node has siblings - return; - } - - // - A parent can be removed if it doesn't have a background - // - A parent can be removed if has a background *and* the child does not have a - // background (in which case, just move the background over to the child, remove - // the parent) - // - If both child and parent have a background, the parent cannot be removed (a - // background can be translucent, have transparent padding, etc.) - boolean nodeHasBackground = element.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND); - boolean parentHasBackground = parent.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND); - if (nodeHasBackground && parentHasBackground) { - // Can't remove because both define a background, and they might both be - // visible (e.g. through transparency or padding). - return; - } - - // Certain parents are special - such as the TabHost and the GestureOverlayView - - // where we want to leave things alone. - if (!CONTAINERS.contains(parentTag)) { - return; - } - - boolean hasId = element.hasAttributeNS(ANDROID_URI, ATTR_ID); - Location location = context.getLocation(element); - String tag = element.getTagName(); - String format; - if (hasId) { - format = "This %1$s layout or its %2$s parent is possibly useless"; - } else { - format = "This %1$s layout or its %2$s parent is useless"; - } - if (nodeHasBackground || parentHasBackground) { - format += "; transfer the background attribute to the other view"; - } - String message = String.format(format, tag, parentTag); - context.report(USELESS_PARENT, element, location, message, null); - } - - // This is the old UselessView check from layoutopt - private static void checkUselessLeaf(XmlContext context, Element element) { - assert LintUtils.getChildCount(element) == 0; - - // Conditions: - // - The node is a container view (LinearLayout, etc.) - // - The node has no id - // - The node has no background - // - The node has no children - // - The node has no style - // - The node is not a root - - if (element.hasAttributeNS(ANDROID_URI, ATTR_ID)) { - return; - } - - if (element.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND)) { - return; - } - - if (element.hasAttribute(ATTR_STYLE)) { - return; - } - - if (element == context.document.getDocumentElement()) { - return; - } - - Location location = context.getLocation(element); - String tag = element.getTagName(); - String message = String.format( - "This %1$s view is useless (no children, no background, no id, no style)", tag); - context.report(USELESS_LEAF, element, location, message, null); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/Utf8Detector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/Utf8Detector.java deleted file mode 100644 index 2e23483..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/Utf8Detector.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -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.android.tools.lint.detector.api.XmlContext; - -import org.w3c.dom.Document; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Checks that the encoding used in resource files is always UTF-8. - */ -public class Utf8Detector extends LayoutDetector { - /** Detects non-utf8 encodings */ - public static final Issue ISSUE = Issue.create( - "EnforceUTF8", //$NON-NLS-1$ - "Checks that all XML resource files are using UTF-8 as the file encoding", - "XML supports encoding in a wide variety of character sets. However, not all " + - "tools handle the XML encoding attribute correctly, and nearly all Android " + - "apps use UTF-8, so by using UTF-8 you can protect yourself against subtle " + - "bugs when using non-ASCII characters.", - Category.I18N, - 2, - Severity.WARNING, - Utf8Detector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** See http://www.w3.org/TR/REC-xml/#NT-EncodingDecl */ - private static final Pattern ENCODING_PATTERN = - Pattern.compile("encoding=['\"](\\S*)['\"]");//$NON-NLS-1$ - - /** Constructs a new {@link Utf8Detector} */ - public Utf8Detector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public void visitDocument(@NonNull XmlContext context, @NonNull Document document) { - String xml = context.getContents(); - if (xml == null) { - return; - } - - // AAPT: The prologue must be in the first line - int lineEnd = 0; - int max = xml.length(); - for (; lineEnd < max; lineEnd++) { - char c = xml.charAt(lineEnd); - if (c == '\n' || c == '\r') { - break; - } - } - - for (int i = 16; i < lineEnd - 5; i++) { // +4: Skip at least <?xml encoding=" - if ((xml.charAt(i) == 'u' || xml.charAt(i) == 'U') - && (xml.charAt(i + 1) == 't' || xml.charAt(i + 1) == 'T') - && (xml.charAt(i + 2) == 'f' || xml.charAt(i + 2) == 'F') - && (xml.charAt(i + 3) == '-' || xml.charAt(i + 3) == '_') - && (xml.charAt(i + 4) == '8')) { - return; - } - } - - int encodingIndex = xml.lastIndexOf("encoding", lineEnd); //$NON-NLS-1$ - if (encodingIndex != -1) { - Matcher matcher = ENCODING_PATTERN.matcher(xml); - if (matcher.find(encodingIndex)) { - String encoding = matcher.group(1); - Location location = Location.create(context.file, xml, - matcher.start(1), matcher.end(1)); - context.report(ISSUE, null, location, String.format( - "%1$s: Not using UTF-8 as the file encoding. This can lead to subtle " + - "bugs with non-ascii characters", encoding), null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ViewConstructorDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ViewConstructorDetector.java deleted file mode 100644 index 4bffdd7..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ViewConstructorDetector.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Detector; -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 org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; - -import java.io.File; -import java.util.List; - -/** - * Looks for custom views that do not define the view constructors needed by UI builders - */ -public class ViewConstructorDetector extends Detector implements Detector.ClassScanner { - private static final String SIG1 = - "(Landroid/content/Context;)V"; //$NON-NLS-1$ - private static final String SIG2 = - "(Landroid/content/Context;Landroid/util/AttributeSet;)V"; //$NON-NLS-1$ - private static final String SIG3 = - "(Landroid/content/Context;Landroid/util/AttributeSet;I)V"; //$NON-NLS-1$ - - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "ViewConstructor", //$NON-NLS-1$ - "Checks that custom views define the expected constructors", - - "Some layout tools (such as the Android layout editor for Eclipse) needs to " + - "find a constructor with one of the following signatures:\n" + - "* `View(Context context)`\n" + - "* `View(Context context, AttributeSet attrs)`\n" + - "* `View(Context context, AttributeSet attrs, int defStyle)`\n" + - "\n" + - "If your custom view needs to perform initialization which does not apply when " + - "used in a layout editor, you can surround the given code with a check to " + - "see if `View#isInEditMode()` is false, since that method will return `false` " + - "at runtime but true within a user interface editor.", - - Category.USABILITY, - 3, - Severity.WARNING, - ViewConstructorDetector.class, - Scope.CLASS_FILE_SCOPE); - - /** Constructs a new {@link ViewConstructorDetector} check */ - public ViewConstructorDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements ClassScanner ---- - - @Override - public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { - if (classNode.name.indexOf('$') != -1 - && (classNode.access & Opcodes.ACC_STATIC) == 0) { - // Ignore inner classes that aren't static: we can't create these - // anyway since we'd need the outer instance - return; - } - - // Ignore abstract classes - if ((classNode.access & Opcodes.ACC_ABSTRACT) != 0) { - return; - } - - if (isViewClass(context, classNode)) { - checkConstructors(context, classNode); - } - } - - private static boolean isViewClass(ClassContext context, ClassNode node) { - String superName = node.superName; - while (superName != null) { - if (superName.equals("android/view/View") //$NON-NLS-1$ - || superName.equals("android/view/ViewGroup") //$NON-NLS-1$ - || superName.startsWith("android/widget/") //$NON-NLS-1$ - && !((superName.endsWith("Adapter") //$NON-NLS-1$ - || superName.endsWith("Controller") //$NON-NLS-1$ - || superName.endsWith("Service") //$NON-NLS-1$ - || superName.endsWith("Provider") //$NON-NLS-1$ - || superName.endsWith("Filter")))) { //$NON-NLS-1$ - return true; - } - - superName = context.getDriver().getSuperClass(superName); - } - - return false; - } - - private static void checkConstructors(ClassContext context, ClassNode classNode) { - // Look through constructors - @SuppressWarnings("rawtypes") - List methods = classNode.methods; - for (Object methodObject : methods) { - MethodNode method = (MethodNode) methodObject; - if (method.name.equals(CONSTRUCTOR_NAME)) { - String desc = method.desc; - if (desc.equals(SIG1) || desc.equals(SIG2) || desc.equals(SIG3)) { - return; - } - } - } - - // If we get this far, none of the expected constructors were found. - - // Use location of one of the constructors? - String message = String.format( - "Custom view %1$s is missing constructor used by tools: " + - "(Context) or (Context,AttributeSet) or (Context,AttributeSet,int)", - classNode.name); - File sourceFile = context.getSourceFile(); - Location location = Location.create(sourceFile != null - ? sourceFile : context.file); - context.report(ISSUE, location, message, null /*data*/); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ViewTagDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ViewTagDetector.java deleted file mode 100644 index 46e24cc..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ViewTagDetector.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * 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 com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.LintDriver; -import com.android.tools.lint.client.api.SdkInfo; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -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 org.objectweb.asm.Type; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.analysis.Analyzer; -import org.objectweb.asm.tree.analysis.AnalyzerException; -import org.objectweb.asm.tree.analysis.BasicInterpreter; -import org.objectweb.asm.tree.analysis.BasicValue; -import org.objectweb.asm.tree.analysis.Frame; - -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; - -/** - * Checks for missing view tag detectors - */ -public class ViewTagDetector extends Detector implements ClassScanner { - /** Using setTag and leaking memory */ - public static final Issue ISSUE = Issue.create( - "ViewTag", //$NON-NLS-1$ - "Finds potential leaks when using View.setTag", - - "Prior to Android 4.0, the implementation of View.setTag(int, Object) would " + - "store the objects in a static map, where the values were strongly referenced. " + - "This means that if the object contains any references pointing back to the " + - "context, the context (which points to pretty much everything else) will leak. " + - "If you pass a view, the view provides a reference to the context " + - "that created it. Similarly, view holders typically contain a view, and cursors " + - "are sometimes also associated with views.", - - Category.PERFORMANCE, - 6, - Severity.WARNING, - ViewTagDetector.class, - EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.CLASS_FILE)); - - /** Constructs a new {@link ViewTagDetector} */ - public ViewTagDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements ClassScanner ---- - - @Override - @Nullable - public List<String> getApplicableCallNames() { - return Collections.singletonList("setTag"); //$NON-NLS-1$ - } - - @Override - public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode call) { - // The leak behavior is fixed in ICS: - // http://code.google.com/p/android/issues/detail?id=18273 - if (context.getMainProject().getMinSdk() >= 14) { - return; - } - - String owner = call.owner; - String desc = call.desc; - if (owner.equals("android/view/View") //$NON-NLS-1$ - && desc.equals("(ILjava/lang/Object;)V")) { //$NON-NLS-1$ - Analyzer analyzer = new Analyzer(new BasicInterpreter() { - @Override - public BasicValue newValue(Type type) { - if (type == null) { - return BasicValue.UNINITIALIZED_VALUE; - } else if (type.getSort() == Type.VOID) { - return null; - } else { - return new BasicValue(type); - } - } - }); - try { - Frame[] frames = analyzer.analyze(classNode.name, method); - InsnList instructions = method.instructions; - Frame frame = frames[instructions.indexOf(call)]; - if (frame.getStackSize() < 3) { - return; - } - BasicValue stackValue = (BasicValue) frame.getStack(2); - Type type = stackValue.getType(); - if (type == null) { - return; - } - - String internalName = type.getInternalName(); - String className = type.getClassName(); - LintDriver driver = context.getDriver(); - - SdkInfo sdkInfo = context.getClient().getSdkInfo(context.getMainProject()); - String objectType = null; - while (className != null) { - if (className.equals("android.view.View")) { //$NON-NLS-1$ - objectType = "views"; - break; - } else if (className.endsWith("ViewHolder")) { //$NON-NLS-1$ - objectType = "view holders"; - break; - } else if (className.endsWith("Cursor") //$NON-NLS-1$ - && className.startsWith("android.")) { //$NON-NLS-1$ - objectType = "cursors"; - break; - } - - // TBD: Bitmaps, drawables? That's tricky, because as explained in - // http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html - // apparently these are used along with nulling out the callbacks, - // and that's harder to detect here - - String parent = sdkInfo.getParentViewClass(className); - if (parent == null) { - if (internalName == null) { - internalName = className.replace('.', '/'); - } - assert internalName != null; - parent = driver.getSuperClass(internalName); - } - className = parent; - internalName = null; - } - - if (objectType != null) { - Location location = context.getLocation(call); - String message = String.format("Avoid setting %1$s as values for setTag: " + - "Can lead to memory leaks in versions older than Android 4.0", - objectType); - context.report(ISSUE, method, call, location, message, null); - } - } catch (AnalyzerException e) { - context.log(e, null); - } - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ViewTypeDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ViewTypeDetector.java deleted file mode 100644 index bad5d50..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ViewTypeDetector.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ATTR_CLASS; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.DOT_JAVA; -import static com.android.SdkConstants.ID_PREFIX; -import static com.android.SdkConstants.NEW_ID_PREFIX; -import static com.android.SdkConstants.VIEW_TAG; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.ResourceXmlDetector; -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 com.google.common.base.Joiner; - -import org.w3c.dom.Attr; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import lombok.ast.AstVisitor; -import lombok.ast.Cast; -import lombok.ast.Expression; -import lombok.ast.MethodInvocation; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; - -/** Detector for finding inconsistent usage of views and casts */ -public class ViewTypeDetector extends ResourceXmlDetector implements Detector.JavaScanner { - /** Mismatched view types */ - public static final Issue ISSUE = Issue.create("WrongViewCast", //$NON-NLS-1$ - "Looks for incorrect casts to views that according to the XML are of a different type", - "Keeps track of the view types associated with ids and if it finds a usage of " + - "the id in the Java code it ensures that it is treated as the same type.", - Category.CORRECTNESS, - 9, - Severity.ERROR, - ViewTypeDetector.class, - EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.ALL_JAVA_FILES)); - - private final Map<String, Object> mIdToViewTag = new HashMap<String, Object>(50); - - @NonNull - @Override - public Speed getSpeed() { - return Speed.SLOW; - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.LAYOUT; - } - - @Override - public boolean appliesTo(@NonNull Context context, @NonNull File file) { - if (LintUtils.endsWith(file.getName(), DOT_JAVA)) { - return true; - } - - return super.appliesTo(context, file); - } - - @Override - public Collection<String> getApplicableAttributes() { - return Collections.singletonList(ATTR_ID); - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - String view = attribute.getOwnerElement().getTagName(); - String value = attribute.getValue(); - String id = null; - if (value.startsWith(ID_PREFIX)) { - id = value.substring(ID_PREFIX.length()); - } else if (value.startsWith(NEW_ID_PREFIX)) { - id = value.substring(NEW_ID_PREFIX.length()); - } // else: could be @android id - - if (id != null) { - if (view.equals(VIEW_TAG)) { - view = attribute.getOwnerElement().getAttribute(ATTR_CLASS); - } - - Object existing = mIdToViewTag.get(id); - if (existing == null) { - mIdToViewTag.put(id, view); - } else if (existing instanceof String) { - String existingString = (String) existing; - if (!existingString.equals(view)) { - // Convert to list - List<String> list = new ArrayList<String>(2); - list.add((String) existing); - list.add(view); - mIdToViewTag.put(id, list); - } - } else if (existing instanceof List<?>) { - @SuppressWarnings("unchecked") - List<String> list = (List<String>) existing; - if (!list.contains(view)) { - list.add(view); - } - } - } - } - - // ---- Implements Detector.JavaScanner ---- - - @Override - public List<String> getApplicableMethodNames() { - return Collections.singletonList("findViewById"); //$NON-NLS-1$ - } - - @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - assert node.astName().getDescription().equals("findViewById"); - if (node.getParent() instanceof Cast) { - Cast cast = (Cast) node.getParent(); - String castType = cast.astTypeReference().getTypeName(); - StrictListAccessor<Expression, MethodInvocation> args = node.astArguments(); - if (args.size() == 1) { - Expression first = args.first(); - // TODO: Do flow analysis as in the StringFormatDetector in order - // to handle variable references too - if (first instanceof Select) { - String resource = first.toString(); - if (resource.startsWith("R.id.")) { //$NON-NLS-1$ - String id = ((Select) first).astIdentifier().astValue(); - Object types = mIdToViewTag.get(id); - if (types instanceof String) { - String layoutType = (String) types; - checkCompatible(context, castType, layoutType, null, cast); - } else if (types instanceof List<?>) { - @SuppressWarnings("unchecked") - List<String> layoutTypes = (List<String>) types; - checkCompatible(context, castType, null, layoutTypes, cast); - } - } - } - } - } - } - - /** Check if the view and cast type are compatible */ - private static void checkCompatible(JavaContext context, String castType, String layoutType, - List<String> layoutTypes, Cast node) { - assert layoutType == null || layoutTypes == null; // Should only specify one or the other - boolean compatible = true; - if (layoutType != null) { - if (!layoutType.equals(castType) - && !context.getSdkInfo().isSubViewOf(castType, layoutType)) { - compatible = false; - } - } else { - compatible = false; - assert layoutTypes != null; - for (String type : layoutTypes) { - if (type.equals(castType) - || context.getSdkInfo().isSubViewOf(castType, type)) { - compatible = true; - break; - } - } - } - - if (!compatible) { - if (layoutType == null) { - layoutType = Joiner.on("|").join(layoutTypes); - } - String message = String.format( - "Unexpected cast to %1$s: layout tag was %2$s", - castType, layoutType); - context.report(ISSUE, node, context.parser.getLocation(context, node), message, - null); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java deleted file mode 100644 index 47410eb..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java +++ /dev/null @@ -1,429 +0,0 @@ -/* - * 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.ANDROID_APP_ACTIVITY; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.checks.ControlFlowGraph.Node; -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.Issue; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Scope; -import com.android.tools.lint.detector.api.Severity; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.JumpInsnNode; -import org.objectweb.asm.tree.LdcInsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.analysis.AnalyzerException; - -import java.util.Arrays; -import java.util.List; - -/** - * Checks for problems with wakelocks (such as failing to release them) - * which can lead to unnecessary battery usage. - */ -public class WakelockDetector extends Detector implements ClassScanner { - - /** Problems using wakelocks */ - public static final Issue ISSUE = Issue.create( - "Wakelock", //$NON-NLS-1$ - "Looks for problems with wakelock usage", - - "Failing to release a wakelock properly can keep the Android device in " + - "a high power mode, which reduces battery life. There are several causes " + - "of this, such as releasing the wake lock in `onDestroy()` instead of in " + - "`onPause()`, failing to call `release()` in all possible code paths after " + - "an `acquire()`, and so on.\n" + - "\n" + - "NOTE: If you are using the lock just to keep the screen on, you should " + - "strongly consider using `FLAG_KEEP_SCREEN_ON` instead. This window flag " + - "will be correctly managed by the platform as the user moves between " + - "applications and doesn't require a special permission. See " + - "http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_KEEP_SCREEN_ON.", - - Category.PERFORMANCE, - 9, - Severity.WARNING, - WakelockDetector.class, - Scope.CLASS_FILE_SCOPE); - - private static final String WAKELOCK_OWNER = "android/os/PowerManager$WakeLock"; //$NON-NLS-1$ - private static final String RELEASE_METHOD = "release"; //$NON-NLS-1$ - private static final String ACQUIRE_METHOD = "acquire"; //$NON-NLS-1$ - private static final String IS_HELD_METHOD = "isHeld"; //$NON-NLS-1$ - private static final String POWER_MANAGER = "android/os/PowerManager"; //$NON-NLS-1$ - private static final String NEW_WAKE_LOCK_METHOD = "newWakeLock"; //$NON-NLS-1$ - - /** Print diagnostics during analysis (display flow control graph etc). - * Make sure you add the asm-debug or asm-util jars to the runtime classpath - * as well since the opcode integer to string mapping display routine looks for - * it via reflection. */ - private static final boolean DEBUG = false; - - /** Constructs a new {@link WakelockDetector} */ - public WakelockDetector() { - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (mHasAcquire && !mHasRelease && context.getDriver().getPhase() == 1) { - // Gather positions of the acquire calls - context.getDriver().requestRepeat(this, Scope.CLASS_FILE_SCOPE); - } - } - - // ---- Implements ClassScanner ---- - - /** Whether any {@code acquire()} calls have been encountered */ - private boolean mHasAcquire; - - /** Whether any {@code release()} calls have been encountered */ - private boolean mHasRelease; - - @Override - @Nullable - public List<String> getApplicableCallNames() { - return Arrays.asList(ACQUIRE_METHOD, RELEASE_METHOD, NEW_WAKE_LOCK_METHOD); - } - - @Override - public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode call) { - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - if (call.owner.equals(WAKELOCK_OWNER)) { - String name = call.name; - if (name.equals(ACQUIRE_METHOD)) { - mHasAcquire = true; - - if (context.getDriver().getPhase() == 2) { - assert !mHasRelease; - context.report(ISSUE, method, call, context.getLocation(call), - "Found a wakelock acquire() but no release() calls anywhere", - null); - } else { - assert context.getDriver().getPhase() == 1; - // Perform flow analysis in this method to see if we're - // performing an acquire/release block, where there are code paths - // between the acquire and release which can result in the - // release call not getting reached. - checkFlow(context, classNode, method, call); - } - } else if (name.equals(RELEASE_METHOD)) { - mHasRelease = true; - - // See if the release is happening in an onDestroy method, in an - // activity. - if ("onDestroy".equals(method.name) //$NON-NLS-1$ - && context.getDriver().isSubclassOf( - classNode, ANDROID_APP_ACTIVITY)) { - context.report(ISSUE, method, call, context.getLocation(call), - "Wakelocks should be released in onPause, not onDestroy", - null); - } - } - } else if (call.owner.equals(POWER_MANAGER)) { - if (call.name.equals(NEW_WAKE_LOCK_METHOD)) { - AbstractInsnNode prev = LintUtils.getPrevInstruction(call); - if (prev == null) { - return; - } - prev = LintUtils.getPrevInstruction(prev); - if (prev == null || prev.getOpcode() != Opcodes.LDC) { - return; - } - LdcInsnNode ldc = (LdcInsnNode) prev; - Object constant = ldc.cst; - if (constant instanceof Integer) { - int flag = ((Integer) constant).intValue(); - // Constant values are copied into the bytecode so we have to compare - // values; however, that means the values are part of the API - final int PARTIAL_WAKE_LOCK = 0x00000001; - final int ACQUIRE_CAUSES_WAKEUP = 0x10000000; - final int both = PARTIAL_WAKE_LOCK | ACQUIRE_CAUSES_WAKEUP; - if ((flag & both) == both) { - context.report(ISSUE, method, call, context.getLocation(call), - "Should not set both PARTIAL_WAKE_LOCK and ACQUIRE_CAUSES_WAKEUP. " - + "If you do not want the screen to turn on, get rid of " - + "ACQUIRE_CAUSES_WAKEUP", - null); - } - } - - } - } - } - - private void checkFlow(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode acquire) { - // Track allocations such that we know whether the type of the call - // is on a SecureRandom rather than a Random - final InsnList instructions = method.instructions; - MethodInsnNode release = null; - - // Find release call - for (int i = 0, n = instructions.size(); i < n; i++) { - AbstractInsnNode instruction = instructions.get(i); - int type = instruction.getType(); - if (type == AbstractInsnNode.METHOD_INSN) { - MethodInsnNode call = (MethodInsnNode) instruction; - if (call.name.equals(RELEASE_METHOD) && - call.owner.equals(WAKELOCK_OWNER)) { - release = call; - break; - } - } - } - - if (release == null) { - // Didn't find both acquire and release in this method; no point in doing - // local flow analysis - return; - } - - try { - MyGraph graph = new MyGraph(); - ControlFlowGraph.create(graph, classNode, method); - - if (DEBUG) { - // Requires util package - //ClassNode clazz = classNode; - //clazz.accept(new TraceClassVisitor(new PrintWriter(System.out))); - System.out.println(graph.toString(graph.getNode(acquire))); - } - - int status = dfs(graph.getNode(acquire)); - if ((status & SEEN_RETURN) != 0) { - String message; - if ((status & SEEN_EXCEPTION) != 0) { - message = "The release() call is not always reached (via exceptional flow)"; - } else { - message = "The release() call is not always reached"; - } - - context.report(ISSUE, method, acquire, - context.getLocation(release), message, null); - } - } catch (AnalyzerException e) { - context.log(e, null); - } - } - - private static final int SEEN_TARGET = 1; - private static final int SEEN_BRANCH = 2; - private static final int SEEN_EXCEPTION = 4; - private static final int SEEN_RETURN = 8; - - /** TODO RENAME */ - private static class MyGraph extends ControlFlowGraph { - @Override - protected void add(@NonNull AbstractInsnNode from, @NonNull AbstractInsnNode to) { - if (from.getOpcode() == Opcodes.IFNULL) { - JumpInsnNode jump = (JumpInsnNode) from; - if (jump.label == to) { - // Skip jump targets on null if it's surrounding the release call - // - // if (lock != null) { - // lock.release(); - // } - // - // The above shouldn't be considered a scenario where release() may not - // be called. - AbstractInsnNode next = LintUtils.getNextInstruction(from); - if (next != null && next.getType() == AbstractInsnNode.VAR_INSN) { - next = LintUtils.getNextInstruction(next); - if (next != null && next.getType() == AbstractInsnNode.METHOD_INSN) { - MethodInsnNode method = (MethodInsnNode) next; - if (method.name.equals(RELEASE_METHOD) && - method.owner.equals(WAKELOCK_OWNER)) { - // This isn't entirely correct; this will also trigger - // for "if (lock == null) { lock.release(); }" but that's - // not likely (and caught by other null checking in tools) - return; - } - } - } - } - } else if (from.getOpcode() == Opcodes.IFEQ) { - JumpInsnNode jump = (JumpInsnNode) from; - if (jump.label == to) { - AbstractInsnNode prev = LintUtils.getPrevInstruction(from); - if (prev != null && prev.getType() == AbstractInsnNode.METHOD_INSN) { - MethodInsnNode method = (MethodInsnNode) prev; - if (method.name.equals(IS_HELD_METHOD) && - method.owner.equals(WAKELOCK_OWNER)) { - AbstractInsnNode next = LintUtils.getNextInstruction(from); - if (next != null) { - super.add(from, next); - return; - } - } - } - } - } - - super.add(from, to); - } - } - - /** Search from the given node towards the target; return false if we reach - * an exit point such as a return or a call on the way there that is not within - * a try/catch clause. - * - * @param node the current node - * @return true if the target was reached - * XXX RETURN VALUES ARE WRONG AS OF RIGHT NOW - */ - protected int dfs(ControlFlowGraph.Node node) { - AbstractInsnNode instruction = node.instruction; - if (instruction.getType() == AbstractInsnNode.JUMP_INSN) { - int opcode = instruction.getOpcode(); - if (opcode == Opcodes.RETURN || opcode == Opcodes.ARETURN - || opcode == Opcodes.LRETURN || opcode == Opcodes.IRETURN - || opcode == Opcodes.DRETURN || opcode == Opcodes.FRETURN - || opcode == Opcodes.ATHROW) { - if (DEBUG) { - System.out.println("Found exit via explicit return: " //$NON-NLS-1$ - + node.toString(false)); - } - return SEEN_RETURN; - } - } - - if (!DEBUG) { - // There are no cycles, so no *NEED* for this, though it does avoid - // researching shared labels. However, it makes debugging harder (no re-entry) - // so this is only done when debugging is off - if (node.visit != 0) { - return 0; - } - node.visit = 1; - } - - // Look for the target. This is any method call node which is a release on the - // lock (later also check it's the same instance, though that's harder). - // This is because finally blocks tend to be inlined so from a single try/catch/finally - // with a release() in the finally, the bytecode can contain multiple repeated - // (inlined) release() calls. - if (instruction.getType() == AbstractInsnNode.METHOD_INSN) { - MethodInsnNode method = (MethodInsnNode) instruction; - if (method.name.equals(RELEASE_METHOD) && method.owner.equals(WAKELOCK_OWNER)) { - return SEEN_TARGET; - } else if (method.name.equals(ACQUIRE_METHOD) && method.owner.equals(WAKELOCK_OWNER)) { - // OK - } else if (method.name.equals(IS_HELD_METHOD) && method.owner.equals(WAKELOCK_OWNER)) { - // OK - } else { - // Some non acquire/release method call: if this is not associated with a - // try-catch block, it would mean the exception would exit the method, - // which would be an error - if (node.exceptions == null || node.exceptions.isEmpty()) { - // Look up the corresponding frame, if any - AbstractInsnNode curr = method.getPrevious(); - boolean foundFrame = false; - while (curr != null) { - if (curr.getType() == AbstractInsnNode.FRAME) { - foundFrame = true; - break; - } - curr = curr.getPrevious(); - } - - if (!foundFrame) { - if (DEBUG) { - System.out.println("Found exit via unguarded method call: " //$NON-NLS-1$ - + node.toString(false)); - } - return SEEN_RETURN; - } - } - } - } - - // if (node.instruction is a call, and the call is not caught by - // a try/catch block (provided the release is not inside the try/catch block) - // then return false - int status = 0; - - boolean implicitReturn = true; - List<Node> successors = node.successors; - List<Node> exceptions = node.exceptions; - if (exceptions != null) { - if (!exceptions.isEmpty()) { - implicitReturn = false; - } - for (Node successor : exceptions) { - status = dfs(successor) | status; - if ((status & SEEN_RETURN) != 0) { - if (DEBUG) { - System.out.println("Found exit via exception: " //$NON-NLS-1$ - + node.toString(false)); - } - return status; - } - } - - if (status != 0) { - status |= SEEN_EXCEPTION; - } - } - - if (successors != null) { - if (!successors.isEmpty()) { - implicitReturn = false; - if (successors.size() > 1) { - status |= SEEN_BRANCH; - } - } - for (Node successor : successors) { - status = dfs(successor) | status; - if ((status & SEEN_RETURN) != 0) { - if (DEBUG) { - System.out.println("Found exit via branches: " //$NON-NLS-1$ - + node.toString(false)); - } - return status; - } - } - } - - if (implicitReturn) { - status |= SEEN_RETURN; - if (DEBUG) { - System.out.println("Found exit: via implicit return: " //$NON-NLS-1$ - + node.toString(false)); - } - } - - return status; - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongCallDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongCallDetector.java deleted file mode 100644 index 174f915..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongCallDetector.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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.tools.lint.checks.JavaPerformanceDetector.ON_DRAW; -import static com.android.tools.lint.checks.JavaPerformanceDetector.ON_LAYOUT; -import static com.android.tools.lint.checks.JavaPerformanceDetector.ON_MEASURE; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.ClassContext; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -import com.android.tools.lint.detector.api.Issue; -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 org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; - -import java.util.Arrays; -import java.util.List; - -/** - * Checks for cases where the wrong call is being made - */ -public class WrongCallDetector extends Detector implements ClassScanner { - /** Calling the wrong method */ - public static final Issue ISSUE = Issue.create( - "WrongCall", //$NON-NLS-1$ - "Finds cases where the wrong call is made, such as calling onMeasure instead of measure", - - "Custom views typically need to call `measure()` on their children, not `onMeasure`. " + - "Ditto for onDraw, onLayout, etc.", - - Category.CORRECTNESS, - 6, - Severity.ERROR, - WrongCallDetector.class, - Scope.CLASS_FILE_SCOPE); - - /** Constructs a new {@link WrongCallDetector} */ - public WrongCallDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements ClassScanner ---- - - @Override - @Nullable - public List<String> getApplicableCallNames() { - return Arrays.asList( - ON_DRAW, - ON_MEASURE, - ON_LAYOUT - ); - } - - @Override - public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, - @NonNull MethodNode method, @NonNull MethodInsnNode call) { - String name = call.name; - // Call is only allowed if it is both only called on the super class (invoke special) - // as well as within the same overriding method (e.g. you can't call super.onLayout - // from the onMeasure method) - if (call.getOpcode() != Opcodes.INVOKESPECIAL || !name.equals(method.name)) { - String suggestion = Character.toLowerCase(name.charAt(2)) + name.substring(3); - String message = String.format( - "Suspicious method call; should probably call \"%1$s\" rather than \"%2$s\"", - suggestion, name); - context.report(ISSUE, method, call, context.getLocation(call), message, null); - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongCaseDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongCaseDetector.java deleted file mode 100644 index d15af9a..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongCaseDetector.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2013 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.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.Arrays; -import java.util.Collection; - -/** - * Check which looks for missing wrong case usage for certain layout tags. - * - * @todo Generalize this to handling spelling errors in general. - */ -public class WrongCaseDetector extends LayoutDetector { - /** Using the wrong case for layout tags */ - public static final Issue WRONGCASE = Issue.create( - "WrongCase", //$NON-NLS-1$ - "Ensures that the correct case is used for special layout tags such as <fragment>", - - "" - + "Most layout tags, such as <Button>, refer to actual view classes and are therefore " - + "capitalized. However, there are exceptions such as <fragment> and <include>. This " - + "lint check looks for incorrect capitalizations.", - - Category.CORRECTNESS, - 8, - Severity.WARNING, - WrongCaseDetector.class, - Scope.RESOURCE_FILE_SCOPE) - .setMoreInfo("http://developer.android.com/guide/components/fragments.html"); //$NON-NLS-1$ - - /** Constructs a new {@link WrongCaseDetector} */ - public WrongCaseDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList( - "Fragment", //$NON-NLS-1$ - "RequestFocus", //$NON-NLS-1$ - "Include", //$NON-NLS-1$ - "Merge" - ); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String tag = element.getTagName(); - String correct = Character.toLowerCase(tag.charAt(0)) + tag.substring(1); - context.report(WRONGCASE, element, context.getLocation(element), - String.format("Invalid tag <%1$s>; should be <%2$s>", tag, correct), null); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongIdDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongIdDetector.java deleted file mode 100644 index e0e2d0a..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongIdDetector.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_TYPE; -import static com.android.SdkConstants.ID_PREFIX; -import static com.android.SdkConstants.NEW_ID_PREFIX; -import static com.android.SdkConstants.RELATIVE_LAYOUT; -import static com.android.SdkConstants.TAG_ITEM; -import static com.android.SdkConstants.VALUE_ID; -import static com.android.tools.lint.detector.api.LintUtils.stripIdPrefix; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.lint.client.api.IDomParser; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.LayoutDetector; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Location.Handle; -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 com.android.utils.Pair; -import com.google.common.base.Joiner; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Checks for duplicate ids within a layout and within an included layout - */ -public class WrongIdDetector extends LayoutDetector { - - /** Ids bound to widgets in any of the layout files */ - private final Set<String> mGlobalIds = new HashSet<String>(100); - - /** Ids bound to widgets in the current layout file */ - private Set<String> mFileIds; - - /** Ids declared in a value's file, e.g. {@code <item type="id" name="foo"/>} */ - private Set<String> mDeclaredIds; - - /** - * Location handles for the various id references that were not found as - * defined in the same layout, to be checked after the whole project has - * been scanned - */ - private List<Pair<String, Location.Handle>> mHandles; - - /** List of RelativeLayout elements in the current layout */ - private List<Element> mRelativeLayouts; - - /** Reference to an unknown id */ - public static final Issue UNKNOWN_ID = Issue.create( - "UnknownId", //$NON-NLS-1$ - "Checks for id references in RelativeLayouts that are not defined elsewhere", - "The `@+id/` syntax refers to an existing id, or creates a new one if it has " + - "not already been defined elsewhere. However, this means that if you have a " + - "typo in your reference, or if the referred view no longer exists, you do not " + - "get a warning since the id will be created on demand. This check catches " + - "errors where you have renamed an id without updating all of the references to " + - "it.", - Category.CORRECTNESS, - 8, - Severity.FATAL, - WrongIdDetector.class, - Scope.ALL_RESOURCES_SCOPE); - - /** Reference to an id that is not in the current layout */ - public static final Issue UNKNOWN_ID_LAYOUT = Issue.create( - "UnknownIdInLayout", //$NON-NLS-1$ - "Makes sure that @+id references refer to views in the same layout", - - "The `@+id/` syntax refers to an existing id, or creates a new one if it has " + - "not already been defined elsewhere. However, this means that if you have a " + - "typo in your reference, or if the referred view no longer exists, you do not " + - "get a warning since the id will be created on demand.\n" + - "\n" + - "This is sometimes intentional, for example where you are referring to a view " + - "which is provided in a different layout via an include. However, it is usually " + - "an accident where you have a typo or you have renamed a view without updating " + - "all the references to it.", - - Category.CORRECTNESS, - 5, - Severity.WARNING, - WrongIdDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a duplicate id check */ - public WrongIdDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.LAYOUT || folderType == ResourceFolderType.VALUES; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection<String> getApplicableAttributes() { - return Collections.singletonList(ATTR_ID); - } - - @Override - public Collection<String> getApplicableElements() { - return Arrays.asList(RELATIVE_LAYOUT, TAG_ITEM); - } - - @Override - public void beforeCheckFile(@NonNull Context context) { - mFileIds = new HashSet<String>(); - mRelativeLayouts = null; - } - - @Override - public void afterCheckFile(@NonNull Context context) { - if (mRelativeLayouts != null) { - if (!context.getProject().getReportIssues()) { - // If this is a library project not being analyzed, ignore it - return; - } - - for (Element layout : mRelativeLayouts) { - NodeList children = layout.getChildNodes(); - for (int j = 0, childCount = children.getLength(); j < childCount; j++) { - Node child = children.item(j); - if (child.getNodeType() != Node.ELEMENT_NODE) { - continue; - } - Element element = (Element) child; - NamedNodeMap attributes = element.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Attr attr = (Attr) attributes.item(i); - String value = attr.getValue(); - if ((value.startsWith(NEW_ID_PREFIX) || - value.startsWith(ID_PREFIX)) - && ANDROID_URI.equals(attr.getNamespaceURI()) - && attr.getLocalName().startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)) { - if (!idDefined(mFileIds, value)) { - // Stash a reference to this id and location such that - // we can check after the *whole* layout has been processed, - // since it's too early to conclude here that the id does - // not exist (you are allowed to have forward references) - XmlContext xmlContext = (XmlContext) context; - IDomParser parser = xmlContext.parser; - Handle handle = parser.createLocationHandle(xmlContext, attr); - handle.setClientData(attr); - - if (mHandles == null) { - mHandles = new ArrayList<Pair<String,Handle>>(); - } - mHandles.add(Pair.of(value, handle)); - } - } - } - } - } - } - - mFileIds = null; - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (mHandles != null) { - boolean checkSameLayout = context.isEnabled(UNKNOWN_ID_LAYOUT); - boolean checkExists = context.isEnabled(UNKNOWN_ID); - boolean projectScope = context.getScope().contains(Scope.ALL_RESOURCE_FILES); - for (Pair<String, Handle> pair : mHandles) { - String id = pair.getFirst(); - boolean isBound = idDefined(mGlobalIds, id); - if (!isBound && checkExists && projectScope) { - Handle handle = pair.getSecond(); - boolean isDeclared = idDefined(mDeclaredIds, id); - id = stripIdPrefix(id); - String suggestionMessage; - List<String> suggestions = getSpellingSuggestions(id, mGlobalIds); - if (suggestions.size() > 1) { - suggestionMessage = String.format(" Did you mean one of {%2$s} ?", - id, Joiner.on(", ").join(suggestions)); - } else if (!suggestions.isEmpty()) { - suggestionMessage = String.format(" Did you mean %2$s ?", - id, suggestions.get(0)); - } else { - suggestionMessage = ""; - } - String message; - if (isDeclared) { - message = String.format( - "The id \"%1$s\" is defined but not assigned to any views.%2$s", - id, suggestionMessage); - } else { - message = String.format( - "The id \"%1$s\" is not defined anywhere.%2$s", - id, suggestionMessage); - } - report(context, UNKNOWN_ID, handle, message); - } else if (checkSameLayout && (!projectScope || isBound) - && id.startsWith(NEW_ID_PREFIX)) { - // The id was defined, but in a different layout. Usually not intentional - // (might be referring to a random other view that happens to have the same - // name.) - Handle handle = pair.getSecond(); - report(context, UNKNOWN_ID_LAYOUT, handle, - String.format( - "The id \"%1$s\" is not referring to any views in this layout", - stripIdPrefix(id))); - } - } - } - } - - private static void report(Context context, Issue issue, Handle handle, String message) { - Location location = handle.resolve(); - Object clientData = handle.getClientData(); - if (clientData instanceof Node) { - if (context.getDriver().isSuppressed(issue, (Node) clientData)) { - return; - } - } - - context.report(issue, location, message, null); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (element.getTagName().equals(RELATIVE_LAYOUT)) { - if (mRelativeLayouts == null) { - mRelativeLayouts = new ArrayList<Element>(); - } - mRelativeLayouts.add(element); - } else { - assert element.getTagName().equals(TAG_ITEM); - String type = element.getAttribute(ATTR_TYPE); - if (VALUE_ID.equals(type)) { - String name = element.getAttribute(ATTR_NAME); - if (!name.isEmpty()) { - if (mDeclaredIds == null) { - mDeclaredIds = Sets.newHashSet(); - } - mDeclaredIds.add(ID_PREFIX + name); - } - } - } - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - assert attribute.getName().equals(ATTR_ID) || attribute.getLocalName().equals(ATTR_ID); - String id = attribute.getValue(); - mFileIds.add(id); - mGlobalIds.add(id); - } - - private static boolean idDefined(Set<String> ids, String id) { - if (ids == null) { - return false; - } - boolean definedLocally = ids.contains(id); - if (!definedLocally) { - if (id.startsWith(NEW_ID_PREFIX)) { - definedLocally = ids.contains(ID_PREFIX + - id.substring(NEW_ID_PREFIX.length())); - } else if (id.startsWith(ID_PREFIX)) { - definedLocally = ids.contains(NEW_ID_PREFIX + - id.substring(ID_PREFIX.length())); - } - } - - return definedLocally; - } - - private static List<String> getSpellingSuggestions(String id, Collection<String> ids) { - int maxDistance = id.length() >= 4 ? 2 : 1; - - // Look for typos and try to match with custom views and android views - Multimap<Integer, String> matches = ArrayListMultimap.create(2, 10); - int count = 0; - if (!ids.isEmpty()) { - for (String matchWith : ids) { - matchWith = stripIdPrefix(matchWith); - if (Math.abs(id.length() - matchWith.length()) > maxDistance) { - // The string lengths differ more than the allowed edit distance; - // no point in even attempting to compute the edit distance (requires - // O(n*m) storage and O(n*m) speed, where n and m are the string lengths) - continue; - } - int distance = LintUtils.editDistance(id, matchWith); - if (distance <= maxDistance) { - matches.put(distance, matchWith); - } - - if (count++ > 100) { - // Make sure that for huge projects we don't completely grind to a halt - break; - } - } - } - - for (int i = 0; i < maxDistance; i++) { - Collection<String> strings = matches.get(i); - if (strings != null && !strings.isEmpty()) { - List<String> suggestions = new ArrayList<String>(strings); - Collections.sort(suggestions); - return suggestions; - } - } - - return Collections.emptyList(); - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongImportDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongImportDetector.java deleted file mode 100644 index 64f3ce1..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongImportDetector.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.Detector; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.JavaContext; -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 java.util.Collections; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.ImportDeclaration; -import lombok.ast.Node; - -/** - * Checks for "import android.R", which seems to be a common source of confusion - * (see for example http://stackoverflow.com/questions/885009/r-cannot-be-resolved-android-error - * and many other forums). - * <p> - * The root cause is probably this (from http://source.android.com/source/using-eclipse.html) : - * <blockquote> Note: Eclipse sometimes likes to add an import android.R - * statement at the top of your files that use resources, especially when you - * ask eclipse to sort or otherwise manage imports. This will cause your make to - * break. Look out for these erroneous import statements and delete them. - * </blockquote> - */ -public class WrongImportDetector extends Detector implements Detector.JavaScanner { - /** Is android.R being imported? */ - public static final Issue ISSUE = Issue.create("SuspiciousImport", //$NON-NLS-1$ - "Checks for 'import android.R' statements, which are usually accidental", - "Importing `android.R` is usually not intentional; it sometimes happens when " + - "you use an IDE and ask it to automatically add imports at a time when your " + - "project's R class it not present.\n" + - "\n" + - "Once the import is there you might get a lot of \"confusing\" error messages " + - "because of course the fields available on `android.R` are not the ones you'd " + - "expect from just looking at your own `R` class.", - Category.CORRECTNESS, - 9, - Severity.WARNING, - WrongImportDetector.class, - Scope.JAVA_FILE_SCOPE); - - /** Constructs a new {@link WrongImportDetector} check */ - public WrongImportDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements Detector.JavaScanner ---- - - @Override - public List<Class<? extends Node>> getApplicableNodeTypes() { - return Collections.<Class<? extends Node>> singletonList( - ImportDeclaration.class); - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - return new ImportVisitor(context); - } - - private static class ImportVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; - - public ImportVisitor(JavaContext context) { - super(); - mContext = context; - } - - @Override - public boolean visitImportDeclaration(ImportDeclaration node) { - String fqn = node.asFullyQualifiedName(); - if (fqn.equals("android.R")) { //$NON-NLS-1$ - Location location = mContext.getLocation(node); - mContext.report(ISSUE, node, location, - "Don't include android.R here; use a fully qualified name for " - + "each usage instead", null); - } - return false; - } - } -} diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongLocationDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongLocationDetector.java deleted file mode 100644 index 37ffbeb..0000000 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/WrongLocationDetector.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.TAG_RESOURCES; - -import com.android.annotations.NonNull; -import com.android.tools.lint.detector.api.Category; -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.Document; -import org.w3c.dom.Element; - -/** Looks for problems with XML files being placed in the wrong folder */ -public class WrongLocationDetector extends LayoutDetector { - /** Main issue investigated by this detector */ - public static final Issue ISSUE = Issue.create( - "WrongFolder", //$NON-NLS-1$ - - "Finds resource files that are placed in the wrong folders", - - "Resource files are sometimes placed in the wrong folder, and it can lead to " + - "subtle bugs that are hard to understand. This check looks for problems in this " + - "area, such as attempting to place a layout \"alias\" file in a `layout/` folder " + - "rather than the `values/` folder where it belongs.", - Category.CORRECTNESS, - 8, - Severity.ERROR, - WrongLocationDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** Constructs a new {@link WrongLocationDetector} check */ - public WrongLocationDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public void visitDocument(@NonNull XmlContext context, @NonNull Document document) { - Element root = document.getDocumentElement(); - if (root != null && root.getTagName().equals(TAG_RESOURCES)) { - context.report(ISSUE, root, context.getLocation(root), - "This file should be placed in a values/ folder, not a layout/ folder", null); - } - } -} diff --git a/manifmerger/Android.mk b/manifmerger/Android.mk index b1f1ac7..b07af36 100644 --- a/manifmerger/Android.mk +++ b/manifmerger/Android.mk @@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # The manifest-merger code has moved to tools/base/manifmerger. -# The rule below uses the prebuilt manifmerger.jar if found. +# The rule below uses the prebuilt manifmerger.jar. # # If you want to run the tests, cd to tools/base/manifmerger # and run ./gradlew :manifmerger:test @@ -26,7 +26,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LIBRARIES := common sdklib LOCAL_PREBUILT_JAVA_LIBRARIES := \ - ../../prebuilts/devtools/$(LOCAL_MODULE)/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) + ../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) include $(BUILD_HOST_PREBUILT) diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java index 894408c..5dc7e7b 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java +++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java @@ -66,6 +66,9 @@ public class MonkeyDevice extends PyObject implements ClassDictInit { @MonkeyRunnerExported(doc = "Sends a DOWN event, immediately followed by an UP event when used with touch() or press()") public static final String DOWN_AND_UP = TouchPressType.DOWN_AND_UP.getIdentifier(); + @MonkeyRunnerExported(doc = "Sends a MOVE event when used with touch().") + public static final String MOVE = TouchPressType.MOVE.getIdentifier(); + private IChimpDevice impl; public MonkeyDevice(IChimpDevice impl) { diff --git a/sdk_common/.classpath b/sdk_common/.classpath index 1afd216..e5bbc54 100644 --- a/sdk_common/.classpath +++ b/sdk_common/.classpath @@ -6,6 +6,6 @@ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/misc/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-13.0.1.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/guava-tools/src.zip"/> <classpathentry combineaccessrules="false" kind="src" path="/common"/> - <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> + <classpathentry combineaccessrules="false" kind="src" path="/sdklib"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/sdk_common/Android.mk b/sdk_common/Android.mk index 51c5919..17c78cb 100644 --- a/sdk_common/Android.mk +++ b/sdk_common/Android.mk @@ -16,7 +16,11 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under,src) +# The sdk_common code has moved to tools/base/sdk_common. +# The rule below uses the prebuilt sdk_common.jar. +# +# If you want to run the tests, cd to tools/base/sdk_common +# and run ./gradlew :sdk_common:test LOCAL_JAVA_LIBRARIES := \ layoutlib_api \ @@ -26,7 +30,10 @@ LOCAL_JAVA_LIBRARIES := \ sdklib LOCAL_MODULE := sdk_common - LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_JAVA_LIBRARY) +LOCAL_PREBUILT_JAVA_LIBRARIES := \ + ../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) + +include $(BUILD_HOST_PREBUILT) + diff --git a/sdk_common/src/com/android/ide/common/rendering/HardwareConfigHelper.java b/sdk_common/src/com/android/ide/common/rendering/HardwareConfigHelper.java deleted file mode 100644 index afca8fe..0000000 --- a/sdk_common/src/com/android/ide/common/rendering/HardwareConfigHelper.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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.ide.common.rendering; - -import com.android.annotations.NonNull; -import com.android.ide.common.rendering.api.HardwareConfig; -import com.android.resources.ScreenOrientation; -import com.android.sdklib.devices.ButtonType; -import com.android.sdklib.devices.Device; -import com.android.sdklib.devices.Screen; - -/** - * Helper method to create a {@link HardwareConfig} object. - * - * The base data comes from a {@link Device} object, with additional data provided on the helper - * object. - * - * Since {@link HardwareConfig} is immutable, this allows creating one in several (optional) - * steps more easily. - * - */ -public class HardwareConfigHelper { - - private final @NonNull Device mDevice; - private @NonNull ScreenOrientation mScreenOrientation = ScreenOrientation.PORTRAIT; - - // optional - private int mMaxRenderWidth = -1; - private int mMaxRenderHeight = -1; - private int mOverrideRenderWidth = -1; - private int mOverrideRenderHeight = -1; - - /** - * Creates a new helper for a given device. - * @param device the device to provide the base data. - */ - public HardwareConfigHelper(@NonNull Device device) { - mDevice = device; - } - - /** - * Sets the orientation of the config. - * @param screenOrientation the orientation. - * @return this (such that chains of setters can be stringed together) - */ - @NonNull - public HardwareConfigHelper setOrientation(@NonNull ScreenOrientation screenOrientation) { - mScreenOrientation = screenOrientation; - return this; - } - - /** - * Overrides the width and height to be used during rendering. - * - * A value of -1 will make the rendering use the normal width and height coming from the - * {@link Device} object. - * - * @param overrideRenderWidth the width in pixels of the layout to be rendered - * @param overrideRenderHeight the height in pixels of the layout to be rendered - * @return this (such that chains of setters can be stringed together) - */ - @NonNull - public HardwareConfigHelper setOverrideRenderSize(int overrideRenderWidth, - int overrideRenderHeight) { - mOverrideRenderWidth = overrideRenderWidth; - mOverrideRenderHeight = overrideRenderHeight; - return this; - } - - /** - * Sets the max width and height to be used during rendering. - * - * A value of -1 will make the rendering use the normal width and height coming from the - * {@link Device} object. - * - * @param maxRenderWidth the max width in pixels of the layout to be rendered - * @param maxRenderHeight the max height in pixels of the layout to be rendered - * @return this (such that chains of setters can be stringed together) - */ - @NonNull - public HardwareConfigHelper setMaxRenderSize(int maxRenderWidth, int maxRenderHeight) { - mMaxRenderWidth = maxRenderWidth; - mMaxRenderHeight = maxRenderHeight; - return this; - } - - /** - * Creates and returns the HardwareConfig object. - * @return the config - */ - @NonNull - public HardwareConfig getConfig() { - Screen screen = mDevice.getDefaultHardware().getScreen(); - - // compute width and height to take orientation into account. - int x = screen.getXDimension(); - int y = screen.getYDimension(); - int width, height; - - if (x > y) { - if (mScreenOrientation == ScreenOrientation.LANDSCAPE) { - width = x; - height = y; - } else { - width = y; - height = x; - } - } else { - if (mScreenOrientation == ScreenOrientation.LANDSCAPE) { - width = y; - height = x; - } else { - width = x; - height = y; - } - } - - if (mOverrideRenderHeight != -1) { - width = mOverrideRenderWidth; - } - - if (mOverrideRenderHeight != -1) { - height = mOverrideRenderHeight; - } - - if (mMaxRenderWidth != -1) { - width = mMaxRenderWidth; - } - - if (mMaxRenderHeight != -1) { - height = mMaxRenderHeight; - } - - return new HardwareConfig( - width, - height, - screen.getPixelDensity(), - (float) screen.getXdpi(), - (float) screen.getYdpi(), - screen.getSize(), - mScreenOrientation, - mDevice.getDefaultHardware().getButtonType() == ButtonType.SOFT); - } -} diff --git a/sdk_common/src/com/android/ide/common/rendering/LayoutLibrary.java b/sdk_common/src/com/android/ide/common/rendering/LayoutLibrary.java deleted file mode 100644 index 0a353f9..0000000 --- a/sdk_common/src/com/android/ide/common/rendering/LayoutLibrary.java +++ /dev/null @@ -1,755 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering; - -import static com.android.ide.common.rendering.api.Result.Status.ERROR_REFLECTION; - -import com.android.ide.common.rendering.api.Bridge; -import com.android.ide.common.rendering.api.Capability; -import com.android.ide.common.rendering.api.DrawableParams; -import com.android.ide.common.rendering.api.ILayoutPullParser; -import com.android.ide.common.rendering.api.LayoutLog; -import com.android.ide.common.rendering.api.RenderSession; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.rendering.api.Result; -import com.android.ide.common.rendering.api.Result.Status; -import com.android.ide.common.rendering.api.SessionParams; -import com.android.ide.common.rendering.api.SessionParams.RenderingMode; -import com.android.ide.common.rendering.api.ViewInfo; -import com.android.ide.common.rendering.legacy.ILegacyPullParser; -import com.android.ide.common.rendering.legacy.LegacyCallback; -import com.android.ide.common.resources.ResourceResolver; -import com.android.ide.common.sdk.LoadStatus; -import com.android.layoutlib.api.ILayoutBridge; -import com.android.layoutlib.api.ILayoutLog; -import com.android.layoutlib.api.ILayoutResult; -import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo; -import com.android.layoutlib.api.IProjectCallback; -import com.android.layoutlib.api.IResourceValue; -import com.android.layoutlib.api.IXmlPullParser; -import com.android.resources.ResourceType; -import com.android.utils.ILogger; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.net.URI; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Class to use the Layout library. - * <p/> - * Use {@link #load(String, ILogger)} to load the jar file. - * <p/> - * Use the layout library with: - * {@link #init(String, Map)}, {@link #supports(Capability)}, {@link #createSession(SessionParams)}, - * {@link #dispose()}, {@link #clearCaches(Object)}. - * - * <p/> - * For client wanting to access both new and old (pre API level 5) layout libraries, it is - * important that the following interfaces be used:<br> - * {@link ILegacyPullParser} instead of {@link ILayoutPullParser}<br> - * {@link LegacyCallback} instead of {@link com.android.ide.common.rendering.api.IProjectCallback}. - * <p/> - * These interfaces will ensure that both new and older Layout libraries can be accessed. - */ -@SuppressWarnings("deprecation") -public class LayoutLibrary { - - public final static String CLASS_BRIDGE = "com.android.layoutlib.bridge.Bridge"; //$NON-NLS-1$ - - /** Link to the layout bridge */ - private final Bridge mBridge; - /** Link to a ILayoutBridge in case loaded an older library */ - private final ILayoutBridge mLegacyBridge; - /** Status of the layoutlib.jar loading */ - private final LoadStatus mStatus; - /** Message associated with the {@link LoadStatus}. This is mostly used when - * {@link #getStatus()} returns {@link LoadStatus#FAILED}. - */ - private final String mLoadMessage; - /** classloader used to load the jar file */ - private final ClassLoader mClassLoader; - - // Reflection data for older Layout Libraries. - private Method mViewGetParentMethod; - private Method mViewGetBaselineMethod; - private Method mViewParentIndexOfChildMethod; - private Class<?> mMarginLayoutParamClass; - private Field mLeftMarginField; - private Field mTopMarginField; - private Field mRightMarginField; - private Field mBottomMarginField; - - /** - * Returns the {@link LoadStatus} of the loading of the layoutlib jar file. - */ - public LoadStatus getStatus() { - return mStatus; - } - - /** Returns the message associated with the {@link LoadStatus}. This is mostly used when - * {@link #getStatus()} returns {@link LoadStatus#FAILED}. - */ - public String getLoadMessage() { - return mLoadMessage; - } - - /** - * Returns the classloader used to load the classes in the layoutlib jar file. - */ - public ClassLoader getClassLoader() { - return mClassLoader; - } - - /** - * Loads the layoutlib.jar file located at the given path and returns a {@link LayoutLibrary} - * object representing the result. - * <p/> - * If loading failed {@link #getStatus()} will reflect this, and {@link #getBridge()} will - * return null. - * - * @param layoutLibJarOsPath the path of the jar file - * @param log an optional log file. - * @return a {@link LayoutLibrary} object always. - */ - public static LayoutLibrary load(String layoutLibJarOsPath, ILogger log, String toolName) { - - LoadStatus status = LoadStatus.LOADING; - String message = null; - Bridge bridge = null; - ILayoutBridge legacyBridge = null; - ClassLoader classLoader = null; - - try { - // get the URL for the file. - File f = new File(layoutLibJarOsPath); - if (f.isFile() == false) { - if (log != null) { - log.error(null, "layoutlib.jar is missing!"); //$NON-NLS-1$ - } - } else { - URI uri = f.toURI(); - URL url = uri.toURL(); - - // create a class loader. Because this jar reference interfaces - // that are in the editors plugin, it's important to provide - // a parent class loader. - classLoader = new URLClassLoader( - new URL[] { url }, - LayoutLibrary.class.getClassLoader()); - - // load the class - Class<?> clazz = classLoader.loadClass(CLASS_BRIDGE); - if (clazz != null) { - // instantiate an object of the class. - Constructor<?> constructor = clazz.getConstructor(); - if (constructor != null) { - Object bridgeObject = constructor.newInstance(); - if (bridgeObject instanceof Bridge) { - bridge = (Bridge)bridgeObject; - } else if (bridgeObject instanceof ILayoutBridge) { - legacyBridge = (ILayoutBridge) bridgeObject; - } - } - } - - if (bridge == null && legacyBridge == null) { - status = LoadStatus.FAILED; - message = "Failed to load " + CLASS_BRIDGE; //$NON-NLS-1$ - if (log != null) { - log.error(null, - "Failed to load " + //$NON-NLS-1$ - CLASS_BRIDGE + - " from " + //$NON-NLS-1$ - layoutLibJarOsPath); - } - } else { - // mark the lib as loaded, unless it's overridden below. - status = LoadStatus.LOADED; - - // check the API, only if it's not a legacy bridge - if (bridge != null) { - int api = bridge.getApiLevel(); - if (api > Bridge.API_CURRENT) { - status = LoadStatus.FAILED; - message = String.format( - "This version of the rendering library is more recent than your version of %1$s. Please update %1$s", toolName); - } - } - } - } - } catch (Throwable t) { - status = LoadStatus.FAILED; - Throwable cause = t; - while (cause.getCause() != null) { - cause = cause.getCause(); - } - message = "Failed to load the LayoutLib: " + cause.getMessage(); - // log the error. - if (log != null) { - log.error(t, message); - } - } - - return new LayoutLibrary(bridge, legacyBridge, classLoader, status, message); - } - - // ------ Layout Lib API proxy - - /** - * Returns the API level of the layout library. - */ - public int getApiLevel() { - if (mBridge != null) { - return mBridge.getApiLevel(); - } - - if (mLegacyBridge != null) { - return getLegacyApiLevel(); - } - - return 0; - } - - /** - * Returns the revision of the library inside a given (layoutlib) API level. - * The true version number of the library is {@link #getApiLevel()}.{@link #getRevision()} - */ - public int getRevision() { - if (mBridge != null) { - return mBridge.getRevision(); - } - - return 0; - } - - /** - * Returns whether the LayoutLibrary supports a given {@link Capability}. - * @return true if it supports it. - * - * @see Bridge#getCapabilities() - * - */ - public boolean supports(Capability capability) { - if (mBridge != null) { - return mBridge.getCapabilities().contains(capability); - } - - if (mLegacyBridge != null) { - switch (capability) { - case UNBOUND_RENDERING: - // legacy stops at 4. 5 is new API. - return getLegacyApiLevel() == 4; - } - } - - return false; - } - - /** - * Initializes the Layout Library object. This must be called before any other action is taken - * on the instance. - * - * @param platformProperties The build properties for the platform. - * @param fontLocation the location of the fonts in the SDK target. - * @param enumValueMap map attrName => { map enumFlagName => Integer value }. This is typically - * read from attrs.xml in the SDK target. - * @param log a {@link LayoutLog} object. Can be null. - * @return true if success. - * - * @see Bridge#init(String, Map) - */ - public boolean init(Map<String, String> platformProperties, - File fontLocation, - Map<String, Map<String, Integer>> enumValueMap, - LayoutLog log) { - if (mBridge != null) { - return mBridge.init(platformProperties, fontLocation, enumValueMap, log); - } else if (mLegacyBridge != null) { - return mLegacyBridge.init(fontLocation.getAbsolutePath(), enumValueMap); - } - - return false; - } - - /** - * Prepares the layoutlib to unloaded. - * - * @see Bridge#dispose() - */ - public boolean dispose() { - if (mBridge != null) { - return mBridge.dispose(); - } - - return true; - } - - /** - * Starts a layout session by inflating and rendering it. The method returns a - * {@link RenderSession} on which further actions can be taken. - * <p/> - * Before taking further actions on the scene, it is recommended to use - * {@link #supports(Capability)} to check what the scene can do. - * - * @return a new {@link ILayoutScene} object that contains the result of the scene creation and - * first rendering or null if {@link #getStatus()} doesn't return {@link LoadStatus#LOADED}. - * - * @see Bridge#createSession(SessionParams) - */ - public RenderSession createSession(SessionParams params) { - if (mBridge != null) { - RenderSession session = mBridge.createSession(params); - if (params.getExtendedViewInfoMode() && - mBridge.getCapabilities().contains(Capability.EXTENDED_VIEWINFO) == false) { - // Extended view info was requested but the layoutlib does not support it. - // Add it manually. - List<ViewInfo> infoList = session.getRootViews(); - if (infoList != null) { - for (ViewInfo info : infoList) { - addExtendedViewInfo(info); - } - } - } - - return session; - } else if (mLegacyBridge != null) { - return createLegacySession(params); - } - - return null; - } - - /** - * Renders a Drawable. If the rendering is successful, the result image is accessible through - * {@link Result#getData()}. It is of type {@link BufferedImage} - * @param params the rendering parameters. - * @return the result of the action. - */ - public Result renderDrawable(DrawableParams params) { - if (mBridge != null) { - return mBridge.renderDrawable(params); - } - - return Status.NOT_IMPLEMENTED.createResult(); - } - - /** - * Clears the resource cache for a specific project. - * <p/>This cache contains bitmaps and nine patches that are loaded from the disk and reused - * until this method is called. - * <p/>The cache is not configuration dependent and should only be cleared when a - * resource changes (at this time only bitmaps and 9 patches go into the cache). - * - * @param projectKey the key for the project. - * - * @see Bridge#clearCaches(Object) - */ - public void clearCaches(Object projectKey) { - if (mBridge != null) { - mBridge.clearCaches(projectKey); - } else if (mLegacyBridge != null) { - mLegacyBridge.clearCaches(projectKey); - } - } - - /** - * Utility method returning the parent of a given view object. - * - * @param viewObject the object for which to return the parent. - * - * @return a {@link Result} indicating the status of the action, and if success, the parent - * object in {@link Result#getData()} - */ - public Result getViewParent(Object viewObject) { - if (mBridge != null) { - Result r = mBridge.getViewParent(viewObject); - if (r.isSuccess()) { - return r; - } - } - - return getViewParentWithReflection(viewObject); - } - - /** - * Utility method returning the index of a given view in its parent. - * @param viewObject the object for which to return the index. - * - * @return a {@link Result} indicating the status of the action, and if success, the index in - * the parent in {@link Result#getData()} - */ - public Result getViewIndex(Object viewObject) { - if (mBridge != null) { - Result r = mBridge.getViewIndex(viewObject); - if (r.isSuccess()) { - return r; - } - } - - return getViewIndexReflection(viewObject); - } - - // ------ Implementation - - private LayoutLibrary(Bridge bridge, ILayoutBridge legacyBridge, ClassLoader classLoader, - LoadStatus status, String message) { - mBridge = bridge; - mLegacyBridge = legacyBridge; - mClassLoader = classLoader; - mStatus = status; - mLoadMessage = message; - } - - /** - * Returns the API level of the legacy bridge. - * <p/> - * This handles the case where ILayoutBridge does not have a {@link ILayoutBridge#getApiLevel()} - * (at API level 1). - * <p/> - * {@link ILayoutBridge#getApiLevel()} should never called directly. - * - * @return the api level of {@link #mLegacyBridge}. - */ - private int getLegacyApiLevel() { - int apiLevel = 1; - try { - apiLevel = mLegacyBridge.getApiLevel(); - } catch (AbstractMethodError e) { - // the first version of the api did not have this method - // so this is 1 - } - - return apiLevel; - } - - private RenderSession createLegacySession(SessionParams params) { - if (params.getLayoutDescription() instanceof IXmlPullParser == false) { - throw new IllegalArgumentException("Parser must be of type ILegacyPullParser"); - } - if (params.getProjectCallback() instanceof - com.android.layoutlib.api.IProjectCallback == false) { - throw new IllegalArgumentException("Project callback must be of type ILegacyCallback"); - } - - if (params.getResources() instanceof ResourceResolver == false) { - throw new IllegalArgumentException("RenderResources object must be of type ResourceResolver"); - } - - ResourceResolver resources = (ResourceResolver) params.getResources(); - - int apiLevel = getLegacyApiLevel(); - - // create a log wrapper since the older api requires a ILayoutLog - final LayoutLog log = params.getLog(); - ILayoutLog logWrapper = new ILayoutLog() { - - @Override - public void warning(String message) { - log.warning(null, message, null /*data*/); - } - - @Override - public void error(Throwable t) { - log.error(null, "error!", t, null /*data*/); - } - - @Override - public void error(String message) { - log.error(null, message, null /*data*/); - } - }; - - - // convert the map of ResourceValue into IResourceValue. Super ugly but works. - - Map<String, Map<String, IResourceValue>> projectMap = convertMap( - resources.getProjectResources()); - Map<String, Map<String, IResourceValue>> frameworkMap = convertMap( - resources.getFrameworkResources()); - - ILayoutResult result = null; - - if (apiLevel == 4) { - // Final ILayoutBridge API added support for "render full height" - result = mLegacyBridge.computeLayout( - (IXmlPullParser) params.getLayoutDescription(), - params.getProjectKey(), - params.getScreenWidth(), params.getScreenHeight(), - params.getRenderingMode() == RenderingMode.FULL_EXPAND ? true : false, - params.getDensity().getDpiValue(), params.getXdpi(), params.getYdpi(), - resources.getThemeName(), resources.isProjectTheme(), - projectMap, frameworkMap, - (IProjectCallback) params.getProjectCallback(), - logWrapper); - } else if (apiLevel == 3) { - // api 3 add density support. - result = mLegacyBridge.computeLayout( - (IXmlPullParser) params.getLayoutDescription(), params.getProjectKey(), - params.getScreenWidth(), params.getScreenHeight(), - params.getDensity().getDpiValue(), params.getXdpi(), params.getYdpi(), - resources.getThemeName(), resources.isProjectTheme(), - projectMap, frameworkMap, - (IProjectCallback) params.getProjectCallback(), logWrapper); - } else if (apiLevel == 2) { - // api 2 added boolean for separation of project/framework theme - result = mLegacyBridge.computeLayout( - (IXmlPullParser) params.getLayoutDescription(), params.getProjectKey(), - params.getScreenWidth(), params.getScreenHeight(), - resources.getThemeName(), resources.isProjectTheme(), - projectMap, frameworkMap, - (IProjectCallback) params.getProjectCallback(), logWrapper); - } else { - // First api with no density/dpi, and project theme boolean mixed - // into the theme name. - - // change the string if it's a custom theme to make sure we can - // differentiate them - String themeName = resources.getThemeName(); - if (resources.isProjectTheme()) { - themeName = "*" + themeName; //$NON-NLS-1$ - } - - result = mLegacyBridge.computeLayout( - (IXmlPullParser) params.getLayoutDescription(), params.getProjectKey(), - params.getScreenWidth(), params.getScreenHeight(), - themeName, - projectMap, frameworkMap, - (IProjectCallback) params.getProjectCallback(), logWrapper); - } - - // clean up that is not done by the ILayoutBridge itself - legacyCleanUp(); - - return convertToScene(result); - } - - @SuppressWarnings("unchecked") - private Map<String, Map<String, IResourceValue>> convertMap( - Map<ResourceType, Map<String, ResourceValue>> map) { - Map<String, Map<String, IResourceValue>> result = - new HashMap<String, Map<String, IResourceValue>>(); - - for (Entry<ResourceType, Map<String, ResourceValue>> entry : map.entrySet()) { - // ugly case but works. - result.put(entry.getKey().getName(), - (Map) entry.getValue()); - } - - return result; - } - - /** - * Converts a {@link ILayoutResult} to a {@link RenderSession}. - */ - private RenderSession convertToScene(ILayoutResult result) { - - Result sceneResult; - ViewInfo rootViewInfo = null; - - if (result.getSuccess() == ILayoutResult.SUCCESS) { - sceneResult = Status.SUCCESS.createResult(); - ILayoutViewInfo oldRootView = result.getRootView(); - if (oldRootView != null) { - rootViewInfo = convertToViewInfo(oldRootView); - } - } else { - sceneResult = Status.ERROR_UNKNOWN.createResult(result.getErrorMessage()); - } - - // create a BasicLayoutScene. This will return the given values but return the default - // implementation for all method. - // ADT should gracefully handle the default implementations of LayoutScene - return new StaticRenderSession(sceneResult, rootViewInfo, result.getImage()); - } - - /** - * Converts a {@link ILayoutViewInfo} (and its children) to a {@link ViewInfo}. - */ - private ViewInfo convertToViewInfo(ILayoutViewInfo view) { - // create the view info. - ViewInfo viewInfo = new ViewInfo(view.getName(), view.getViewKey(), - view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); - - // then convert the children - ILayoutViewInfo[] children = view.getChildren(); - if (children != null) { - ArrayList<ViewInfo> convertedChildren = new ArrayList<ViewInfo>(children.length); - for (ILayoutViewInfo child : children) { - convertedChildren.add(convertToViewInfo(child)); - } - viewInfo.setChildren(convertedChildren); - } - - return viewInfo; - } - - /** - * Post rendering clean-up that must be done here because it's not done in any layoutlib using - * {@link ILayoutBridge}. - */ - private void legacyCleanUp() { - try { - Class<?> looperClass = mClassLoader.loadClass("android.os.Looper"); //$NON-NLS-1$ - Field threadLocalField = looperClass.getField("sThreadLocal"); //$NON-NLS-1$ - if (threadLocalField != null) { - threadLocalField.setAccessible(true); - // get object. Field is static so no need to pass an object - ThreadLocal<?> threadLocal = (ThreadLocal<?>) threadLocalField.get(null); - if (threadLocal != null) { - threadLocal.remove(); - } - } - } catch (Exception e) { - // do nothing. - } - } - - private Result getViewParentWithReflection(Object viewObject) { - // default implementation using reflection. - try { - if (mViewGetParentMethod == null) { - Class<?> viewClass = Class.forName("android.view.View"); - mViewGetParentMethod = viewClass.getMethod("getParent"); - } - - return Status.SUCCESS.createResult(mViewGetParentMethod.invoke(viewObject)); - } catch (Exception e) { - // Catch all for the reflection calls. - return ERROR_REFLECTION.createResult(null, e); - } - } - - /** - * Utility method returning the index of a given view in its parent. - * @param viewObject the object for which to return the index. - * - * @return a {@link Result} indicating the status of the action, and if success, the index in - * the parent in {@link Result#getData()} - */ - private Result getViewIndexReflection(Object viewObject) { - // default implementation using reflection. - try { - Class<?> viewClass = Class.forName("android.view.View"); - - if (mViewGetParentMethod == null) { - mViewGetParentMethod = viewClass.getMethod("getParent"); - } - - Object parentObject = mViewGetParentMethod.invoke(viewObject); - - if (mViewParentIndexOfChildMethod == null) { - Class<?> viewParentClass = Class.forName("android.view.ViewParent"); - mViewParentIndexOfChildMethod = viewParentClass.getMethod("indexOfChild", - viewClass); - } - - return Status.SUCCESS.createResult( - mViewParentIndexOfChildMethod.invoke(parentObject, viewObject)); - } catch (Exception e) { - // Catch all for the reflection calls. - return ERROR_REFLECTION.createResult(null, e); - } - } - - private void addExtendedViewInfo(ViewInfo info) { - computeExtendedViewInfo(info); - - List<ViewInfo> children = info.getChildren(); - for (ViewInfo child : children) { - addExtendedViewInfo(child); - } - } - - private void computeExtendedViewInfo(ViewInfo info) { - Object viewObject = info.getViewObject(); - Object params = info.getLayoutParamsObject(); - - int baseLine = getViewBaselineReflection(viewObject); - int leftMargin = 0; - int topMargin = 0; - int rightMargin = 0; - int bottomMargin = 0; - - try { - if (mMarginLayoutParamClass == null) { - mMarginLayoutParamClass = Class.forName( - "android.view.ViewGroup$MarginLayoutParams"); - - mLeftMarginField = mMarginLayoutParamClass.getField("leftMargin"); - mTopMarginField = mMarginLayoutParamClass.getField("topMargin"); - mRightMarginField = mMarginLayoutParamClass.getField("rightMargin"); - mBottomMarginField = mMarginLayoutParamClass.getField("bottomMargin"); - } - - if (mMarginLayoutParamClass.isAssignableFrom(params.getClass())) { - - leftMargin = (Integer)mLeftMarginField.get(params); - topMargin = (Integer)mTopMarginField.get(params); - rightMargin = (Integer)mRightMarginField.get(params); - bottomMargin = (Integer)mBottomMarginField.get(params); - } - - } catch (Exception e) { - // just use 'unknown' value. - leftMargin = Integer.MIN_VALUE; - topMargin = Integer.MIN_VALUE; - rightMargin = Integer.MIN_VALUE; - bottomMargin = Integer.MIN_VALUE; - } - - info.setExtendedInfo(baseLine, leftMargin, topMargin, rightMargin, bottomMargin); - } - - /** - * Utility method returning the baseline value for a given view object. This basically returns - * View.getBaseline(). - * - * @param viewObject the object for which to return the index. - * - * @return the baseline value or -1 if not applicable to the view object or if this layout - * library does not implement this method. - */ - private int getViewBaselineReflection(Object viewObject) { - // default implementation using reflection. - try { - if (mViewGetBaselineMethod == null) { - Class<?> viewClass = Class.forName("android.view.View"); - mViewGetBaselineMethod = viewClass.getMethod("getBaseline"); - } - - Object result = mViewGetBaselineMethod.invoke(viewObject); - if (result instanceof Integer) { - return ((Integer)result).intValue(); - } - - } catch (Exception e) { - // Catch all for the reflection calls. - } - - return Integer.MIN_VALUE; - } -} diff --git a/sdk_common/src/com/android/ide/common/rendering/StaticRenderSession.java b/sdk_common/src/com/android/ide/common/rendering/StaticRenderSession.java deleted file mode 100644 index c122c1c..0000000 --- a/sdk_common/src/com/android/ide/common/rendering/StaticRenderSession.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering; - -import com.android.ide.common.rendering.api.RenderSession; -import com.android.ide.common.rendering.api.Result; -import com.android.ide.common.rendering.api.ViewInfo; - -import java.awt.image.BufferedImage; -import java.util.Collections; -import java.util.List; - -/** - * Static {@link RenderSession} returning a given {@link Result}, {@link ViewInfo} and - * {@link BufferedImage}. - * <p/> - * All other methods are untouched from the base implementation provided by the API. - * <p/> - * This is meant to be used as a wrapper around the static results. No further operations are - * possible. - * - */ -public class StaticRenderSession extends RenderSession { - - private final Result mResult; - private final List<ViewInfo> mRootViewInfo; - private final BufferedImage mImage; - - public StaticRenderSession(Result result, ViewInfo rootViewInfo, BufferedImage image) { - mResult = result; - mRootViewInfo = Collections.singletonList(rootViewInfo); - mImage = image; - } - - @Override - public Result getResult() { - return mResult; - } - - @Override - public List<ViewInfo> getRootViews() { - return mRootViewInfo; - } - - @Override - public BufferedImage getImage() { - return mImage; - } -} diff --git a/sdk_common/src/com/android/ide/common/rendering/legacy/ILegacyPullParser.java b/sdk_common/src/com/android/ide/common/rendering/legacy/ILegacyPullParser.java deleted file mode 100644 index a71e190..0000000 --- a/sdk_common/src/com/android/ide/common/rendering/legacy/ILegacyPullParser.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.legacy; - -import com.android.ide.common.rendering.api.ILayoutPullParser; -import com.android.layoutlib.api.IXmlPullParser; - -/** - * Intermediary interface extending both old and new project pull parsers from the layout lib API. - * - * Clients should use this instead of {@link ILayoutPullParser} or {@link IXmlPullParser}. - * - */ -@SuppressWarnings("deprecation") -public interface ILegacyPullParser extends ILayoutPullParser, IXmlPullParser { - -} diff --git a/sdk_common/src/com/android/ide/common/rendering/legacy/LegacyCallback.java b/sdk_common/src/com/android/ide/common/rendering/legacy/LegacyCallback.java deleted file mode 100644 index 67e6a7b..0000000 --- a/sdk_common/src/com/android/ide/common/rendering/legacy/LegacyCallback.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.rendering.legacy; - -import com.android.ide.common.rendering.api.IProjectCallback; -import com.android.resources.ResourceType; -import com.android.util.Pair; - -/** - * Intermediary class implementing parts of both the old and new project call back from the - * layout lib API. - * - * Clients should use this instead of {@link IProjectCallback} to target both old and new - * Layout Libraries. - * - */ -@SuppressWarnings("deprecation") -public abstract class LegacyCallback implements - com.android.ide.common.rendering.api.IProjectCallback, - com.android.layoutlib.api.IProjectCallback { - - // ------ implementation of the old interface using the new interface. - - @Override - public final Integer getResourceValue(String type, String name) { - return getResourceId(ResourceType.getEnum(type), name); - } - - @Override - public final String[] resolveResourceValue(int id) { - Pair<ResourceType, String> info = resolveResourceId(id); - if (info != null) { - return new String[] { info.getSecond(), info.getFirst().getName() }; - } - - return null; - } - - @Override - public final String resolveResourceValue(int[] id) { - return resolveResourceId(id); - } - - // ------ -} diff --git a/sdk_common/src/com/android/ide/common/resources/FrameworkResourceItem.java b/sdk_common/src/com/android/ide/common/resources/FrameworkResourceItem.java deleted file mode 100644 index 70bbcef..0000000 --- a/sdk_common/src/com/android/ide/common/resources/FrameworkResourceItem.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.ide.common.resources; - -/** - * A custom {@link ResourceItem} for resources provided by the framework. - * - * The main change is that {@link #isEditableDirectly()} returns false. - */ -class FrameworkResourceItem extends ResourceItem { - - FrameworkResourceItem(String name) { - super(name); - } - - @Override - public boolean isEditableDirectly() { - return false; - } - - @Override - public String toString() { - return "FrameworkResourceItem [mName=" + getName() + ", mFiles=" //$NON-NLS-1$ //$NON-NLS-2$ - + getSourceFileList() + "]"; //$NON-NLS-1$ - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/FrameworkResources.java b/sdk_common/src/com/android/ide/common/resources/FrameworkResources.java deleted file mode 100755 index fe8e197..0000000 --- a/sdk_common/src/com/android/ide/common/resources/FrameworkResources.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * 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.ide.common.resources; - - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.io.IAbstractFile; -import com.android.io.IAbstractFolder; -import com.android.resources.ResourceType; -import com.android.utils.ILogger; -import com.google.common.base.Charsets; - -import org.kxml2.io.KXmlParser; -import org.xmlpull.v1.XmlPullParser; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; - -/** - * Framework resources repository. - * - * This behaves the same as {@link ResourceRepository} except that it differentiates between - * resources that are public and non public. - * {@link #getResources(ResourceType)} and {@link #hasResourcesOfType(ResourceType)} only return - * public resources. This is typically used to display resource lists in the UI. - * - * {@link #getConfiguredResources(com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration)} - * returns all resources, even the non public ones so that this can be used for rendering. - */ -public class FrameworkResources extends ResourceRepository { - - /** - * Map of {@link ResourceType} to list of items. It is guaranteed to contain a list for all - * possible values of ResourceType. - */ - protected final Map<ResourceType, List<ResourceItem>> mPublicResourceMap = - new EnumMap<ResourceType, List<ResourceItem>>(ResourceType.class); - - public FrameworkResources(@NonNull IAbstractFolder resFolder) { - super(resFolder, true /*isFrameworkRepository*/); - } - - /** - * Returns a {@link Collection} (always non null, but can be empty) of <b>public</b> - * {@link ResourceItem} matching a given {@link ResourceType}. - * - * @param type the type of the resources to return - * @return a collection of items, possible empty. - */ - @Override - @NonNull - public List<ResourceItem> getResourceItemsOfType(@NonNull ResourceType type) { - return mPublicResourceMap.get(type); - } - - /** - * Returns whether the repository has <b>public</b> resources of a given {@link ResourceType}. - * @param type the type of resource to check. - * @return true if the repository contains resources of the given type, false otherwise. - */ - @Override - public boolean hasResourcesOfType(@NonNull ResourceType type) { - return mPublicResourceMap.get(type).size() > 0; - } - - @Override - @NonNull - protected ResourceItem createResourceItem(@NonNull String name) { - return new FrameworkResourceItem(name); - } - - /** - * Reads the public.xml file in data/res/values/ for a given resource folder and builds up - * a map of public resources. - * - * This map is a subset of the full resource map that only contains framework resources - * that are public. - * - * @param resFolder The root folder of the resources - * @param logger a logger to report issues to - */ - public void loadPublicResources(@Nullable ILogger logger) { - IAbstractFolder valueFolder = getResFolder().getFolder(SdkConstants.FD_RES_VALUES); - if (valueFolder.exists() == false) { - return; - } - - IAbstractFile publicXmlFile = valueFolder.getFile("public.xml"); //$NON-NLS-1$ - if (publicXmlFile.exists()) { - Reader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(publicXmlFile.getContents(), - Charsets.UTF_8)); - KXmlParser parser = new KXmlParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - parser.setInput(reader); - - ResourceType lastType = null; - String lastTypeName = ""; - while (true) { - int event = parser.next(); - if (event == XmlPullParser.START_TAG) { - // As of API 15 there are a number of "java-symbol" entries here - if (!parser.getName().equals("public")) { //$NON-NLS-1$ - continue; - } - - String name = null; - String typeName = null; - for (int i = 0, n = parser.getAttributeCount(); i < n; i++) { - String attribute = parser.getAttributeName(i); - - if (attribute.equals("name")) { //$NON-NLS-1$ - name = parser.getAttributeValue(i); - if (typeName != null) { - // Skip id attribute processing - break; - } - } else if (attribute.equals("type")) { //$NON-NLS-1$ - typeName = parser.getAttributeValue(i); - } - } - - if (name != null && typeName != null) { - ResourceType type = null; - if (typeName.equals(lastTypeName)) { - type = lastType; - } else { - type = ResourceType.getEnum(typeName); - lastType = type; - lastTypeName = typeName; - } - if (type != null) { - ResourceItem match = null; - Map<String, ResourceItem> map = mResourceMap.get(type); - if (map != null) { - match = map.get(name); - } - - if (match != null) { - List<ResourceItem> publicList = mPublicResourceMap.get(type); - if (publicList == null) { - // Pick initial size for the list to hold the public - // resources. We could just use map.size() here, - // but they're usually much bigger; for example, - // in one platform version, there are 1500 drawables - // and 1200 strings but only 175 and 25 public ones - // respectively. - int size; - switch (type) { - case STYLE: size = 500; break; - case ATTR: size = 1000; break; - case DRAWABLE: size = 200; break; - case ID: size = 50; break; - case LAYOUT: - case COLOR: - case STRING: - case ANIM: - case INTERPOLATOR: - size = 30; - break; - default: - size = 10; - break; - } - publicList = new ArrayList<ResourceItem>(size); - mPublicResourceMap.put(type, publicList); - } - - publicList.add(match); - } else { - // log that there's a public resource that doesn't actually - // exist? - } - } else { - // log that there was a reference to a typo that doesn't actually - // exist? - } - } - } else if (event == XmlPullParser.END_DOCUMENT) { - break; - } - } - } catch (Exception e) { - if (logger != null) { - logger.error(e, "Can't read and parse public attribute list"); - } - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - // Nothing to be done here - we don't care if it closed or not. - } - } - } - } - - // put unmodifiable list for all res type in the public resource map - // this will simplify access - for (ResourceType type : ResourceType.values()) { - List<ResourceItem> list = mPublicResourceMap.get(type); - if (list == null) { - list = Collections.emptyList(); - } else { - list = Collections.unmodifiableList(list); - } - - // put the new list in the map - mPublicResourceMap.put(type, list); - } - } -} - diff --git a/sdk_common/src/com/android/ide/common/resources/IdGeneratingResourceFile.java b/sdk_common/src/com/android/ide/common/resources/IdGeneratingResourceFile.java deleted file mode 100644 index 9ff1748..0000000 --- a/sdk_common/src/com/android/ide/common/resources/IdGeneratingResourceFile.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.ide.common.resources; - -import com.android.ide.common.rendering.api.DensityBasedResourceValue; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.ValueResourceParser.IValueResourceRepository; -import com.android.ide.common.resources.configuration.DensityQualifier; -import com.android.io.IAbstractFile; -import com.android.io.StreamException; -import com.android.resources.ResourceType; - -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Represents a resource file that also generates ID resources. - * <p/> - * This is typically an XML file in res/layout or res/menu - */ -public final class IdGeneratingResourceFile extends ResourceFile - implements IValueResourceRepository { - - private final Map<String, ResourceValue> mIdResources = - new HashMap<String, ResourceValue>(); - - private final Collection<ResourceType> mResourceTypeList; - - private final String mFileName; - - private final ResourceType mFileType; - - private final ResourceValue mFileValue; - - public IdGeneratingResourceFile(IAbstractFile file, ResourceFolder folder, ResourceType type) { - super(file, folder); - - mFileType = type; - - // Set up our resource types - mResourceTypeList = new HashSet<ResourceType>(); - mResourceTypeList.add(mFileType); - mResourceTypeList.add(ResourceType.ID); - - // compute the resource name - mFileName = getFileName(type); - - // Get the resource value of this file as a whole layout - mFileValue = getFileValue(file, folder); - } - - @Override - protected void load(ScanningContext context) { - // Parse the file and look for @+id/ entries - parseFileForIds(context); - - // create the resource items in the repository - updateResourceItems(context); - } - - @Override - protected void update(ScanningContext context) { - // Copy the previous list of ID names - Set<String> oldIdNames = new HashSet<String>(mIdResources.keySet()); - - // reset current content. - mIdResources.clear(); - - // need to parse the file and find the IDs. - if (!parseFileForIds(context)) { - context.requestFullAapt(); - // Continue through to updating the resource item here since it - // will make for example layout rendering more accurate until - // aapt is re-run - } - - // We only need to update the repository if our IDs have changed - Set<String> keySet = mIdResources.keySet(); - assert keySet != oldIdNames; - if (oldIdNames.equals(keySet) == false) { - updateResourceItems(context); - } - } - - @Override - protected void dispose(ScanningContext context) { - ResourceRepository repository = getRepository(); - - // Remove declarations from this file from the repository - repository.removeFile(mResourceTypeList, this); - - // Ask for an ID refresh since we'll be taking away ID generating items - context.requestFullAapt(); - } - - @Override - public Collection<ResourceType> getResourceTypes() { - return mResourceTypeList; - } - - @Override - public boolean hasResources(ResourceType type) { - return (type == mFileType) || (type == ResourceType.ID && !mIdResources.isEmpty()); - } - - @Override - public ResourceValue getValue(ResourceType type, String name) { - // Check to see if they're asking for one of the right types: - if (type != mFileType && type != ResourceType.ID) { - return null; - } - - // If they're looking for a resource of this type with this name give them the whole file - if (type == mFileType && name.equals(mFileName)) { - return mFileValue; - } else { - // Otherwise try to return them an ID - // the map will return null if it's not found - return mIdResources.get(name); - } - } - - /** - * Looks through the file represented for Ids and adds them to - * our id repository - * - * @return true if parsing succeeds and false if it fails - */ - private boolean parseFileForIds(ScanningContext context) { - IdResourceParser parser = new IdResourceParser(this, context, isFramework()); - try { - IAbstractFile file = getFile(); - return parser.parse(mFileType, file.getOsLocation(), file.getContents()); - } catch (IOException e) { - // Pass - } catch (StreamException e) { - // Pass - } - - return false; - } - - /** - * Add the resources represented by this file to the repository - */ - private void updateResourceItems(ScanningContext context) { - ResourceRepository repository = getRepository(); - - // remove this file from all existing ResourceItem. - repository.removeFile(mResourceTypeList, this); - - // First add this as a layout file - ResourceItem item = repository.getResourceItem(mFileType, mFileName); - item.add(this); - - // Now iterate through our IDs and add - for (String idName : mIdResources.keySet()) { - item = repository.getResourceItem(ResourceType.ID, idName); - // add this file to the list of files generating ID resources. - item.add(this); - } - - // Ask the repository for an ID refresh - context.requestFullAapt(); - } - - /** - * Returns the resource value associated with this whole file as a layout resource - * @param file the file handler that represents this file - * @param folder the folder this file is under - * @return a resource value associated with this layout - */ - private ResourceValue getFileValue(IAbstractFile file, ResourceFolder folder) { - // test if there's a density qualifier associated with the resource - DensityQualifier qualifier = folder.getConfiguration().getDensityQualifier(); - - ResourceValue value; - if (qualifier == null) { - value = new ResourceValue(mFileType, mFileName, - file.getOsLocation(), isFramework()); - } else { - value = new DensityBasedResourceValue( - mFileType, mFileName, - file.getOsLocation(), - qualifier.getValue(), - isFramework()); - } - return value; - } - - - /** - * Returns the name of this resource. - */ - private String getFileName(ResourceType type) { - // get the name from the filename. - String name = getFile().getName(); - - int pos = name.indexOf('.'); - if (pos != -1) { - name = name.substring(0, pos); - } - - return name; - } - - @Override - public void addResourceValue(ResourceValue value) { - // Just overwrite collisions. We're only interested in the unique - // IDs declared - mIdResources.put(value.getName(), value); - } - - @Override - public boolean hasResourceValue(ResourceType type, String name) { - if (type == ResourceType.ID) { - return mIdResources.containsKey(name); - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/IdResourceParser.java b/sdk_common/src/com/android/ide/common/resources/IdResourceParser.java deleted file mode 100644 index 60c1725..0000000 --- a/sdk_common/src/com/android/ide/common/resources/IdResourceParser.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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.ide.common.resources; - -import com.android.annotations.NonNull; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.ValueResourceParser.IValueResourceRepository; -import com.android.resources.ResourceType; -import com.google.common.io.Closeables; - -import org.kxml2.io.KXmlParser; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * Parser for scanning an id-generating resource file such as a layout or a menu - * file, which registers any ids it encounters with an - * {@link IValueResourceRepository}, and which registers errors with a - * {@link ScanningContext}. - */ -public class IdResourceParser { - private final IValueResourceRepository mRepository; - private final boolean mIsFramework; - private ScanningContext mContext; - - /** - * Creates a new {@link IdResourceParser} - * - * @param repository value repository for registering resource declaration - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - * @param isFramework true if scanning a framework resource - */ - public IdResourceParser( - @NonNull IValueResourceRepository repository, - @NonNull ScanningContext context, - boolean isFramework) { - mRepository = repository; - mContext = context; - mIsFramework = isFramework; - } - - /** - * Parse the given input and register ids with the given - * {@link IValueResourceRepository}. - * - * @param type the type of resource being scanned - * @param path the full OS path to the file being parsed - * @param input the input stream of the XML to be parsed (will be closed by this method) - * @return true if parsing succeeds and false if it fails - * @throws IOException if reading the contents fails - */ - public boolean parse(ResourceType type, final String path, InputStream input) - throws IOException { - KXmlParser parser = new KXmlParser(); - try { - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - - if (input instanceof FileInputStream) { - input = new BufferedInputStream(input); - } - parser.setInput(input, "UTF-8"); //$NON-NLS-1$ - - return parse(type, path, parser); - } catch (XmlPullParserException e) { - String message = e.getMessage(); - - // Strip off position description - int index = message.indexOf("(position:"); //$NON-NLS-1$ (Hardcoded in KXml) - if (index != -1) { - message = message.substring(0, index); - } - - String error = String.format("%1$s:%2$d: Error: %3$s", //$NON-NLS-1$ - path, parser.getLineNumber(), message); - mContext.addError(error); - return false; - } catch (RuntimeException e) { - // Some exceptions are thrown by the KXmlParser that are not XmlPullParserExceptions, - // such as this one: - // java.lang.RuntimeException: Undefined Prefix: w in org.kxml2.io.KXmlParser@... - // at org.kxml2.io.KXmlParser.adjustNsp(Unknown Source) - // at org.kxml2.io.KXmlParser.parseStartTag(Unknown Source) - String message = e.getMessage(); - String error = String.format("%1$s:%2$d: Error: %3$s", //$NON-NLS-1$ - path, parser.getLineNumber(), message); - mContext.addError(error); - return false; - } finally { - Closeables.closeQuietly(input); - } - } - - private boolean parse(ResourceType type, String path, KXmlParser parser) - throws XmlPullParserException, IOException { - boolean valid = true; - boolean checkForErrors = !mIsFramework && !mContext.needsFullAapt(); - - while (true) { - int event = parser.next(); - if (event == XmlPullParser.START_TAG) { - for (int i = 0, n = parser.getAttributeCount(); i < n; i++) { - String attribute = parser.getAttributeName(i); - String value = parser.getAttributeValue(i); - assert value != null : attribute; - - if (checkForErrors) { - String uri = parser.getAttributeNamespace(i); - if (!mContext.checkValue(uri, attribute, value)) { - mContext.requestFullAapt(); - checkForErrors = false; - valid = false; - } - } - - if (value.startsWith("@+")) { //$NON-NLS-1$ - // Strip out the @+id/ or @+android:id/ section - String id = value.substring(value.indexOf('/') + 1); - ResourceValue newId = new ResourceValue(ResourceType.ID, id, - mIsFramework); - mRepository.addResourceValue(newId); - } - } - } else if (event == XmlPullParser.END_DOCUMENT) { - break; - } - } - - return valid; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/InlineResourceItem.java b/sdk_common/src/com/android/ide/common/resources/InlineResourceItem.java deleted file mode 100644 index 37fdc81..0000000 --- a/sdk_common/src/com/android/ide/common/resources/InlineResourceItem.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.resources; - -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.resources.ResourceType; - - -/** - * Represents a resource item that has been declared inline in another resource file. - * - * This covers the typical ID declaration of "@+id/foo", but does not cover normal value - * resources declared in strings.xml or other similar value files. - * - * This resource will return {@code true} for {@link #isDeclaredInline()} and {@code false} for - * {@link #isEditableDirectly()}. - */ -public class InlineResourceItem extends ResourceItem { - - private ResourceValue mValue = null; - - /** - * Constructs a new inline ResourceItem. - * @param name the name of the resource as it appears in the XML and R.java files. - */ - public InlineResourceItem(String name) { - super(name); - } - - @Override - public boolean isDeclaredInline() { - return true; - } - - @Override - public boolean isEditableDirectly() { - return false; - } - - @Override - public ResourceValue getResourceValue(ResourceType type, FolderConfiguration referenceConfig, - boolean isFramework) { - assert type == ResourceType.ID; - if (mValue == null) { - mValue = new ResourceValue(type, getName(), isFramework); - } - - return mValue; - } - - @Override - public String toString() { - return "InlineResourceItem [mName=" + getName() + ", mFiles=" //$NON-NLS-1$ //$NON-NLS-2$ - + getSourceFileList() + "]"; //$NON-NLS-1$ - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/IntArrayWrapper.java b/sdk_common/src/com/android/ide/common/resources/IntArrayWrapper.java deleted file mode 100644 index 668c677..0000000 --- a/sdk_common/src/com/android/ide/common/resources/IntArrayWrapper.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.resources; - -import java.util.Arrays; - - -/** - * Wrapper around a int[] to provide hashCode/equals support. - */ -public final class IntArrayWrapper { - - private int[] mData; - - public IntArrayWrapper(int[] data) { - mData = data; - } - - public void set(int[] data) { - mData = data; - } - - @Override - public int hashCode() { - return Arrays.hashCode(mData); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass().equals(obj.getClass())) { - return Arrays.equals(mData, ((IntArrayWrapper)obj).mData); - } - - return super.equals(obj); - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/MultiResourceFile.java b/sdk_common/src/com/android/ide/common/resources/MultiResourceFile.java deleted file mode 100644 index c9a8bc7..0000000 --- a/sdk_common/src/com/android/ide/common/resources/MultiResourceFile.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources; - -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.ValueResourceParser.IValueResourceRepository; -import com.android.io.IAbstractFile; -import com.android.io.StreamException; -import com.android.resources.ResourceType; - -import org.xml.sax.SAXException; - -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.Map; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -/** - * Represents a resource file able to declare multiple resources, which could be of - * different {@link ResourceType}. - * <p/> - * This is typically an XML file inside res/values. - */ -public final class MultiResourceFile extends ResourceFile implements IValueResourceRepository { - - private final static SAXParserFactory sParserFactory = SAXParserFactory.newInstance(); - - private final Map<ResourceType, Map<String, ResourceValue>> mResourceItems = - new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class); - - private Collection<ResourceType> mResourceTypeList = null; - - public MultiResourceFile(IAbstractFile file, ResourceFolder folder) { - super(file, folder); - } - - // Boolean flag to track whether a named element has been added or removed, thus requiring - // a new ID table to be generated - private boolean mNeedIdRefresh; - - @Override - protected void load(ScanningContext context) { - // need to parse the file and find the content. - parseFile(); - - // create new ResourceItems for the new content. - mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet()); - - // We need an ID generation step - mNeedIdRefresh = true; - - // create/update the resource items. - updateResourceItems(context); - } - - @Override - protected void update(ScanningContext context) { - // Reset the ID generation flag - mNeedIdRefresh = false; - - // Copy the previous version of our list of ResourceItems and types - Map<ResourceType, Map<String, ResourceValue>> oldResourceItems - = new EnumMap<ResourceType, Map<String, ResourceValue>>(mResourceItems); - - // reset current content. - mResourceItems.clear(); - - // need to parse the file and find the content. - parseFile(); - - // create new ResourceItems for the new content. - mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet()); - - // Check to see if any names have changed. If so, mark the flag so updateResourceItems - // can notify the ResourceRepository that an ID refresh is needed - if (oldResourceItems.keySet().equals(mResourceItems.keySet())) { - for (ResourceType type : mResourceTypeList) { - // We just need to check the names of the items. - // If there are new or removed names then we'll have to regenerate IDs - if (mResourceItems.get(type).keySet() - .equals(oldResourceItems.get(type).keySet()) == false) { - mNeedIdRefresh = true; - } - } - } else { - // If our type list is different, obviously the names will be different - mNeedIdRefresh = true; - } - // create/update the resource items. - updateResourceItems(context); - } - - @Override - protected void dispose(ScanningContext context) { - ResourceRepository repository = getRepository(); - - // only remove this file from all existing ResourceItem. - repository.removeFile(mResourceTypeList, this); - - // We'll need an ID refresh because we deleted items - context.requestFullAapt(); - - // don't need to touch the content, it'll get reclaimed as this objects disappear. - // In the mean time other objects may need to access it. - } - - @Override - public Collection<ResourceType> getResourceTypes() { - return mResourceTypeList; - } - - @Override - public boolean hasResources(ResourceType type) { - Map<String, ResourceValue> list = mResourceItems.get(type); - return (list != null && list.size() > 0); - } - - private void updateResourceItems(ScanningContext context) { - ResourceRepository repository = getRepository(); - - // remove this file from all existing ResourceItem. - repository.removeFile(mResourceTypeList, this); - - for (ResourceType type : mResourceTypeList) { - Map<String, ResourceValue> list = mResourceItems.get(type); - - if (list != null) { - Collection<ResourceValue> values = list.values(); - for (ResourceValue res : values) { - ResourceItem item = repository.getResourceItem(type, res.getName()); - - // add this file to the list of files generating this resource item. - item.add(this); - } - } - } - - // If we need an ID refresh, ask the repository for that now - if (mNeedIdRefresh) { - context.requestFullAapt(); - } - } - - /** - * Parses the file and creates a list of {@link ResourceType}. - */ - private void parseFile() { - try { - SAXParser parser = sParserFactory.newSAXParser(); - parser.parse(getFile().getContents(), new ValueResourceParser(this, isFramework())); - } catch (ParserConfigurationException e) { - } catch (SAXException e) { - } catch (IOException e) { - } catch (StreamException e) { - } - } - - /** - * Adds a resource item to the list - * @param value The value of the resource. - */ - @Override - public void addResourceValue(ResourceValue value) { - ResourceType resType = value.getResourceType(); - - Map<String, ResourceValue> list = mResourceItems.get(resType); - - // if the list does not exist, create it. - if (list == null) { - list = new HashMap<String, ResourceValue>(); - mResourceItems.put(resType, list); - } else { - // look for a possible value already existing. - ResourceValue oldValue = list.get(value.getName()); - - if (oldValue != null) { - oldValue.replaceWith(value); - return; - } - } - - // empty list or no match found? add the given resource - list.put(value.getName(), value); - } - - @Override - public boolean hasResourceValue(ResourceType type, String name) { - Map<String, ResourceValue> map = mResourceItems.get(type); - return map != null && map.containsKey(name); - } - - @Override - public ResourceValue getValue(ResourceType type, String name) { - // get the list for the given type - Map<String, ResourceValue> list = mResourceItems.get(type); - - if (list != null) { - return list.get(name); - } - - return null; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/ResourceDeltaKind.java b/sdk_common/src/com/android/ide/common/resources/ResourceDeltaKind.java deleted file mode 100644 index 769b6ea..0000000 --- a/sdk_common/src/com/android/ide/common/resources/ResourceDeltaKind.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.ide.common.resources; - -/** - * Enum indicating a type of resource change. - * - * This is similar, and can be easily mapped to Eclipse's integer constants in IResourceDelta. - */ -public enum ResourceDeltaKind { - CHANGED, ADDED, REMOVED; -} diff --git a/sdk_common/src/com/android/ide/common/resources/ResourceFile.java b/sdk_common/src/com/android/ide/common/resources/ResourceFile.java deleted file mode 100644 index 378602a..0000000 --- a/sdk_common/src/com/android/ide/common/resources/ResourceFile.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources; - -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.configuration.Configurable; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.io.IAbstractFile; -import com.android.resources.ResourceType; - -import java.util.Collection; - -/** - * Represents a Resource file (a file under $Project/res/) - */ -public abstract class ResourceFile implements Configurable { - - private final IAbstractFile mFile; - private final ResourceFolder mFolder; - - protected ResourceFile(IAbstractFile file, ResourceFolder folder) { - mFile = file; - mFolder = folder; - } - - protected abstract void load(ScanningContext context); - protected abstract void update(ScanningContext context); - protected abstract void dispose(ScanningContext context); - - @Override - public FolderConfiguration getConfiguration() { - return mFolder.getConfiguration(); - } - - /** - * Returns the IFile associated with the ResourceFile. - */ - public final IAbstractFile getFile() { - return mFile; - } - - /** - * Returns the parent folder as a {@link ResourceFolder}. - */ - public final ResourceFolder getFolder() { - return mFolder; - } - - public final ResourceRepository getRepository() { - return mFolder.getRepository(); - } - - /** - * Returns whether the resource is a framework resource. - */ - public final boolean isFramework() { - return mFolder.getRepository().isFrameworkRepository(); - } - - /** - * Returns the list of {@link ResourceType} generated by the file. This is never null. - */ - public abstract Collection<ResourceType> getResourceTypes(); - - /** - * Returns whether the file generated a resource of a specific type. - * @param type The {@link ResourceType} - */ - public abstract boolean hasResources(ResourceType type); - - /** - * Returns the value of a resource generated by this file by {@link ResourceType} and name. - * <p/>If no resource match, <code>null</code> is returned. - * @param type the type of the resource. - * @param name the name of the resource. - */ - public abstract ResourceValue getValue(ResourceType type, String name); - - @Override - public String toString() { - return mFile.toString(); - } -} - diff --git a/sdk_common/src/com/android/ide/common/resources/ResourceFolder.java b/sdk_common/src/com/android/ide/common/resources/ResourceFolder.java deleted file mode 100644 index d6464c8..0000000 --- a/sdk_common/src/com/android/ide/common/resources/ResourceFolder.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources; - -import com.android.annotations.VisibleForTesting; -import com.android.annotations.VisibleForTesting.Visibility; -import com.android.ide.common.resources.configuration.Configurable; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.io.IAbstractFile; -import com.android.io.IAbstractFolder; -import com.android.resources.FolderTypeRelationship; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Resource Folder class. Contains list of {@link ResourceFile}s, - * the {@link FolderConfiguration}, and a link to the {@link IAbstractFolder} object. - */ -public final class ResourceFolder implements Configurable { - final ResourceFolderType mType; - final FolderConfiguration mConfiguration; - IAbstractFolder mFolder; - List<ResourceFile> mFiles = null; - Map<String, ResourceFile> mNames = null; - private final ResourceRepository mRepository; - - /** - * Creates a new {@link ResourceFolder} - * @param type The type of the folder - * @param config The configuration of the folder - * @param folder The associated {@link IAbstractFolder} object. - * @param repository The associated {@link ResourceRepository} - */ - protected ResourceFolder(ResourceFolderType type, FolderConfiguration config, - IAbstractFolder folder, ResourceRepository repository) { - mType = type; - mConfiguration = config; - mFolder = folder; - mRepository = repository; - } - - /** - * Processes a file and adds it to its parent folder resource. - * - * @param file the underlying resource file. - * @param kind the file change kind. - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - * @return the {@link ResourceFile} that was created. - */ - public ResourceFile processFile(IAbstractFile file, ResourceDeltaKind kind, - ScanningContext context) { - // look for this file if it's already been created - ResourceFile resFile = getFile(file, context); - - if (resFile == null) { - if (kind != ResourceDeltaKind.REMOVED) { - // create a ResourceFile for it. - - resFile = createResourceFile(file); - resFile.load(context); - - // add it to the folder - addFile(resFile); - } - } else { - if (kind == ResourceDeltaKind.REMOVED) { - removeFile(resFile, context); - } else { - resFile.update(context); - } - } - - return resFile; - } - - private ResourceFile createResourceFile(IAbstractFile file) { - // check if that's a single or multi resource type folder. For now we define this by - // the number of possible resource type output by files in the folder. - // We have a special case for layout/menu folders which can also generate IDs. - // This does - // not make the difference between several resource types from a single file or - // the ability to have 2 files in the same folder generating 2 different types of - // resource. The former is handled by MultiResourceFile properly while we don't - // handle the latter. If we were to add this behavior we'd have to change this call. - List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(mType); - - ResourceFile resFile = null; - if (types.size() == 1) { - resFile = new SingleResourceFile(file, this); - } else if (types.contains(ResourceType.LAYOUT)) { - resFile = new IdGeneratingResourceFile(file, this, ResourceType.LAYOUT); - } else if (types.contains(ResourceType.MENU)) { - resFile = new IdGeneratingResourceFile(file, this, ResourceType.MENU); - } else { - resFile = new MultiResourceFile(file, this); - } - return resFile; - } - - /** - * Adds a {@link ResourceFile} to the folder. - * @param file The {@link ResourceFile}. - */ - @VisibleForTesting(visibility=Visibility.PROTECTED) - public void addFile(ResourceFile file) { - if (mFiles == null) { - int initialSize = 16; - if (mRepository.isFrameworkRepository()) { - String name = mFolder.getName(); - // Pick some reasonable initial sizes for framework data structures - // since they are typically (a) large and (b) their sizes are roughly known - // in advance - switch (mType) { - case DRAWABLE: { - // See if it's one of the -mdpi, -hdpi etc folders which - // are large (~1250 items) - int index = name.indexOf('-'); - if (index == -1) { - initialSize = 230; // "drawable" folder - } else { - index = name.indexOf('-', index + 1); - if (index == -1) { - // One of the "drawable-<density>" folders - initialSize = 1260; - } else { - // "drawable-sw600dp-hdpi" etc - initialSize = 30; - } - } - break; - } - case LAYOUT: { - // The main layout folder has about ~185 layouts in it; - // the others are small - if (name.indexOf('-') == -1) { - initialSize = 200; - } - break; - } - case VALUES: { - if (name.indexOf('-') == -1) { - initialSize = 32; - } else { - initialSize = 4; - } - break; - } - case ANIM: initialSize = 85; break; - case COLOR: initialSize = 32; break; - case RAW: initialSize = 4; break; - default: - // Stick with the 16 default - break; - } - } - - mFiles = new ArrayList<ResourceFile>(initialSize); - mNames = new HashMap<String, ResourceFile>(initialSize, 2.0f); - } - - mFiles.add(file); - mNames.put(file.getFile().getName(), file); - } - - protected void removeFile(ResourceFile file, ScanningContext context) { - file.dispose(context); - mFiles.remove(file); - mNames.remove(file.getFile().getName()); - } - - protected void dispose(ScanningContext context) { - if (mFiles != null) { - for (ResourceFile file : mFiles) { - file.dispose(context); - } - - mFiles.clear(); - mNames.clear(); - } - } - - /** - * Returns the {@link IAbstractFolder} associated with this object. - */ - public IAbstractFolder getFolder() { - return mFolder; - } - - /** - * Returns the {@link ResourceFolderType} of this object. - */ - public ResourceFolderType getType() { - return mType; - } - - public ResourceRepository getRepository() { - return mRepository; - } - - /** - * Returns the list of {@link ResourceType}s generated by the files inside this folder. - */ - public Collection<ResourceType> getResourceTypes() { - ArrayList<ResourceType> list = new ArrayList<ResourceType>(); - - if (mFiles != null) { - for (ResourceFile file : mFiles) { - Collection<ResourceType> types = file.getResourceTypes(); - - // loop through those and add them to the main list, - // if they are not already present - for (ResourceType resType : types) { - if (list.indexOf(resType) == -1) { - list.add(resType); - } - } - } - } - - return list; - } - - @Override - public FolderConfiguration getConfiguration() { - return mConfiguration; - } - - /** - * Returns whether the folder contains a file with the given name. - * @param name the name of the file. - */ - public boolean hasFile(String name) { - if (mNames != null && mNames.containsKey(name)) { - return true; - } - - // Note: mNames.containsKey(name) is faster, but doesn't give the same result; this - // method seems to be called on this ResourceFolder before it has been processed, - // so we need to use the file system check instead: - return mFolder.hasFile(name); - } - - /** - * Returns the {@link ResourceFile} matching a {@link IAbstractFile} object. - * - * @param file The {@link IAbstractFile} object. - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - * @return the {@link ResourceFile} or null if no match was found. - */ - private ResourceFile getFile(IAbstractFile file, ScanningContext context) { - assert mFolder.equals(file.getParentFolder()); - - if (mNames != null) { - ResourceFile resFile = mNames.get(file.getName()); - if (resFile != null) { - return resFile; - } - } - - // If the file actually exists, the resource folder may not have been - // scanned yet; add it lazily - if (file.exists()) { - ResourceFile resFile = createResourceFile(file); - resFile.load(context); - addFile(resFile); - return resFile; - } - - return null; - } - - /** - * Returns the {@link ResourceFile} matching a given name. - * @param filename The name of the file to return. - * @return the {@link ResourceFile} or <code>null</code> if no match was found. - */ - public ResourceFile getFile(String filename) { - if (mNames != null) { - ResourceFile resFile = mNames.get(filename); - if (resFile != null) { - return resFile; - } - } - - // If the file actually exists, the resource folder may not have been - // scanned yet; add it lazily - IAbstractFile file = mFolder.getFile(filename); - if (file != null && file.exists()) { - ResourceFile resFile = createResourceFile(file); - resFile.load(new ScanningContext(mRepository)); - addFile(resFile); - return resFile; - } - - return null; - } - - /** - * Returns whether a file in the folder is generating a resource of a specified type. - * @param type The {@link ResourceType} being looked up. - */ - public boolean hasResources(ResourceType type) { - // Check if the folder type is able to generate resource of the type that was asked. - // this is a first check to avoid going through the files. - List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type); - - boolean valid = false; - for (ResourceFolderType rft : folderTypes) { - if (rft == mType) { - valid = true; - break; - } - } - - if (valid) { - if (mFiles != null) { - for (ResourceFile f : mFiles) { - if (f.hasResources(type)) { - return true; - } - } - } - } - return false; - } - - @Override - public String toString() { - return mFolder.toString(); - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/ResourceItem.java b/sdk_common/src/com/android/ide/common/resources/ResourceItem.java deleted file mode 100644 index 49396eb..0000000 --- a/sdk_common/src/com/android/ide/common/resources/ResourceItem.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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.ide.common.resources; - -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.resources.ResourceType; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * An android resource. - * - * This is a representation of the resource, not of its value(s). It gives access to all - * the source files that generate this particular resource which then can be used to access - * the actual value(s). - * - * @see ResourceFile#getResources(ResourceType, ResourceRepository) - */ -public class ResourceItem implements Comparable<ResourceItem> { - - private final static Comparator<ResourceFile> sComparator = new Comparator<ResourceFile>() { - @Override - public int compare(ResourceFile file1, ResourceFile file2) { - // get both FolderConfiguration and compare them - FolderConfiguration fc1 = file1.getFolder().getConfiguration(); - FolderConfiguration fc2 = file2.getFolder().getConfiguration(); - - return fc1.compareTo(fc2); - } - }; - - private final String mName; - - /** - * List of files generating this ResourceItem. - */ - private final List<ResourceFile> mFiles = new ArrayList<ResourceFile>(); - - /** - * Constructs a new ResourceItem. - * @param name the name of the resource as it appears in the XML and R.java files. - */ - public ResourceItem(String name) { - mName = name; - } - - /** - * Returns the name of the resource. - */ - public final String getName() { - return mName; - } - - /** - * Compares the {@link ResourceItem} to another. - * @param other the ResourceItem to be compared to. - */ - @Override - public int compareTo(ResourceItem other) { - return mName.compareTo(other.mName); - } - - /** - * Returns whether the resource is editable directly. - * <p/> - * This is typically the case for resources that don't have alternate versions, or resources - * of type {@link ResourceType#ID} that aren't declared inline. - */ - public boolean isEditableDirectly() { - return hasAlternates() == false; - } - - /** - * Returns whether the ID resource has been declared inline inside another resource XML file. - * If the resource type is not {@link ResourceType#ID}, this will always return {@code false}. - */ - public boolean isDeclaredInline() { - return false; - } - - /** - * Returns a {@link ResourceValue} for this item based on the given configuration. - * If the ResourceItem has several source files, one will be selected based on the config. - * @param type the type of the resource. This is necessary because ResourceItem doesn't embed - * its type, but ResourceValue does. - * @param referenceConfig the config of the resource item. - * @param isFramework whether the resource is a framework value. Same as the type. - * @return a ResourceValue or null if none match the config. - */ - public ResourceValue getResourceValue(ResourceType type, FolderConfiguration referenceConfig, - boolean isFramework) { - // look for the best match for the given configuration - // the match has to be of type ResourceFile since that's what the input list contains - ResourceFile match = (ResourceFile) referenceConfig.findMatchingConfigurable(mFiles); - - if (match != null) { - // get the value of this configured resource. - return match.getValue(type, mName); - } - - return null; - } - - /** - * Adds a new source file. - * @param file the source file. - */ - protected void add(ResourceFile file) { - mFiles.add(file); - } - - /** - * Removes a file from the list of source files. - * @param file the file to remove - */ - protected void removeFile(ResourceFile file) { - mFiles.remove(file); - } - - /** - * Returns {@code true} if the item has no source file. - * @return - */ - protected boolean hasNoSourceFile() { - return mFiles.size() == 0; - } - - /** - * Reset the item by emptying its source file list. - */ - protected void reset() { - mFiles.clear(); - } - - /** - * Returns the sorted list of {@link ResourceItem} objects for this resource item. - */ - public ResourceFile[] getSourceFileArray() { - ArrayList<ResourceFile> list = new ArrayList<ResourceFile>(); - list.addAll(mFiles); - - Collections.sort(list, sComparator); - - return list.toArray(new ResourceFile[list.size()]); - } - - /** - * Returns the list of source file for this resource. - */ - public List<ResourceFile> getSourceFileList() { - return Collections.unmodifiableList(mFiles); - } - - /** - * Returns if the resource has at least one non-default version. - * - * @see ResourceFile#getConfiguration() - * @see FolderConfiguration#isDefault() - */ - public boolean hasAlternates() { - for (ResourceFile file : mFiles) { - if (file.getFolder().getConfiguration().isDefault() == false) { - return true; - } - } - - return false; - } - - /** - * Returns whether the resource has a default version, with no qualifier. - * - * @see ResourceFile#getConfiguration() - * @see FolderConfiguration#isDefault() - */ - public boolean hasDefault() { - for (ResourceFile file : mFiles) { - if (file.getFolder().getConfiguration().isDefault()) { - return true; - } - } - - // We only want to return false if there's no default and more than 0 items. - return (mFiles.size() == 0); - } - - /** - * Returns the number of alternate versions for this resource. - * - * @see ResourceFile#getConfiguration() - * @see FolderConfiguration#isDefault() - */ - public int getAlternateCount() { - int count = 0; - for (ResourceFile file : mFiles) { - if (file.getFolder().getConfiguration().isDefault() == false) { - count++; - } - } - - return count; - } - - /** - * Returns a formatted string usable in an XML to use for the {@link ResourceItem}. - * @param system Whether this is a system resource or a project resource. - * @return a string in the format @[type]/[name] - */ - public String getXmlString(ResourceType type, boolean system) { - if (type == ResourceType.ID && isDeclaredInline()) { - return (system ? "@android:" : "@+") + type.getName() + "/" + mName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - - return (system ? "@android:" : "@") + type.getName() + "/" + mName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - - @Override - public String toString() { - return "ResourceItem [mName=" + mName + ", mFiles=" + mFiles + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/ResourceRepository.java b/sdk_common/src/com/android/ide/common/resources/ResourceRepository.java deleted file mode 100755 index a3b20ed..0000000 --- a/sdk_common/src/com/android/ide/common/resources/ResourceRepository.java +++ /dev/null @@ -1,960 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources; - -import static com.android.SdkConstants.ATTR_REF_PREFIX; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; -import static com.android.SdkConstants.PREFIX_THEME_REF; -import static com.android.SdkConstants.RESOURCE_CLZ_ATTR; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.configuration.Configurable; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.LanguageQualifier; -import com.android.ide.common.resources.configuration.RegionQualifier; -import com.android.io.IAbstractFile; -import com.android.io.IAbstractFolder; -import com.android.io.IAbstractResource; -import com.android.resources.FolderTypeRelationship; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; -import com.android.utils.Pair; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -/** - * Base class for resource repository. - * - * A repository is both a file representation of a resource folder and a representation - * of the generated resources, organized by type. - * - * {@link #getResourceFolder(IAbstractFolder)} and {@link #getSourceFiles(ResourceType, String, FolderConfiguration)} - * give access to the folders and files of the resource folder. - * - * {@link #getResources(ResourceType)} gives access to the resources directly. - * - */ -public abstract class ResourceRepository { - - private final IAbstractFolder mResourceFolder; - - protected Map<ResourceFolderType, List<ResourceFolder>> mFolderMap = - new EnumMap<ResourceFolderType, List<ResourceFolder>>(ResourceFolderType.class); - - protected Map<ResourceType, Map<String, ResourceItem>> mResourceMap = - new EnumMap<ResourceType, Map<String, ResourceItem>>( - ResourceType.class); - - private Map<Map<String, ResourceItem>, Collection<ResourceItem>> mReadOnlyListMap = - new IdentityHashMap<Map<String, ResourceItem>, Collection<ResourceItem>>(); - - private final boolean mFrameworkRepository; - private boolean mCleared = true; - private boolean mInitializing = false; - - protected final IntArrayWrapper mWrapper = new IntArrayWrapper(null); - - /** - * Makes a resource repository - * @param resFolder the resource folder of the repository. - * @param isFrameworkRepository whether the repository is for framework resources. - */ - protected ResourceRepository(@NonNull IAbstractFolder resFolder, - boolean isFrameworkRepository) { - mResourceFolder = resFolder; - mFrameworkRepository = isFrameworkRepository; - } - - public IAbstractFolder getResFolder() { - return mResourceFolder; - } - - public boolean isFrameworkRepository() { - return mFrameworkRepository; - } - - public synchronized void clear() { - mCleared = true; - mFolderMap = new EnumMap<ResourceFolderType, List<ResourceFolder>>( - ResourceFolderType.class); - mResourceMap = new EnumMap<ResourceType, Map<String, ResourceItem>>( - ResourceType.class); - - mReadOnlyListMap = - new IdentityHashMap<Map<String, ResourceItem>, Collection<ResourceItem>>(); - } - - /** - * Ensures that the repository has been initialized again after a call to - * {@link ResourceRepository#clear()} - * - * @return true if the repository was just re-initialized. - */ - public synchronized boolean ensureInitialized() { - if (mCleared && !mInitializing) { - ScanningContext context = new ScanningContext(this); - mInitializing = true; - - IAbstractResource[] resources = mResourceFolder.listMembers(); - - for (IAbstractResource res : resources) { - if (res instanceof IAbstractFolder) { - IAbstractFolder folder = (IAbstractFolder)res; - ResourceFolder resFolder = processFolder(folder); - - if (resFolder != null) { - // now we process the content of the folder - IAbstractResource[] files = folder.listMembers(); - - for (IAbstractResource fileRes : files) { - if (fileRes instanceof IAbstractFile) { - IAbstractFile file = (IAbstractFile)fileRes; - - resFolder.processFile(file, ResourceDeltaKind.ADDED, context); - } - } - } - } - } - - mInitializing = false; - mCleared = false; - return true; - } - - return false; - } - - /** - * Adds a Folder Configuration to the project. - * @param type The resource type. - * @param config The resource configuration. - * @param folder The workspace folder object. - * @return the {@link ResourceFolder} object associated to this folder. - */ - private ResourceFolder add( - @NonNull ResourceFolderType type, - @NonNull FolderConfiguration config, - @NonNull IAbstractFolder folder) { - // get the list for the resource type - List<ResourceFolder> list = mFolderMap.get(type); - - if (list == null) { - list = new ArrayList<ResourceFolder>(); - - ResourceFolder cf = new ResourceFolder(type, config, folder, this); - list.add(cf); - - mFolderMap.put(type, list); - - return cf; - } - - // look for an already existing folder configuration. - for (ResourceFolder cFolder : list) { - if (cFolder.mConfiguration.equals(config)) { - // config already exist. Nothing to be done really, besides making sure - // the IAbstractFolder object is up to date. - cFolder.mFolder = folder; - return cFolder; - } - } - - // If we arrive here, this means we didn't find a matching configuration. - // So we add one. - ResourceFolder cf = new ResourceFolder(type, config, folder, this); - list.add(cf); - - return cf; - } - - /** - * Removes a {@link ResourceFolder} associated with the specified {@link IAbstractFolder}. - * @param type The type of the folder - * @param removedFolder the IAbstractFolder object. - * @param context the scanning context - * @return the {@link ResourceFolder} that was removed, or null if no matches were found. - */ - @Nullable - public ResourceFolder removeFolder( - @NonNull ResourceFolderType type, - @NonNull IAbstractFolder removedFolder, - @Nullable ScanningContext context) { - ensureInitialized(); - - // get the list of folders for the resource type. - List<ResourceFolder> list = mFolderMap.get(type); - - if (list != null) { - int count = list.size(); - for (int i = 0 ; i < count ; i++) { - ResourceFolder resFolder = list.get(i); - IAbstractFolder folder = resFolder.getFolder(); - if (removedFolder.equals(folder)) { - // we found the matching ResourceFolder. we need to remove it. - list.remove(i); - - // remove its content - resFolder.dispose(context); - - return resFolder; - } - } - } - - return null; - } - - /** - * Returns true if this resource repository contains a resource of the given - * name. - * - * @param url the resource URL - * @return true if the resource is known - */ - public boolean hasResourceItem(@NonNull String url) { - // Handle theme references - if (url.startsWith(PREFIX_THEME_REF)) { - String remainder = url.substring(PREFIX_THEME_REF.length()); - if (url.startsWith(ATTR_REF_PREFIX)) { - url = PREFIX_RESOURCE_REF + url.substring(PREFIX_THEME_REF.length()); - return hasResourceItem(url); - } - int colon = url.indexOf(':'); - if (colon != -1) { - // Convert from ?android:progressBarStyleBig to ?android:attr/progressBarStyleBig - if (remainder.indexOf('/', colon) == -1) { - remainder = remainder.substring(0, colon) + RESOURCE_CLZ_ATTR + '/' - + remainder.substring(colon); - } - url = PREFIX_RESOURCE_REF + remainder; - return hasResourceItem(url); - } else { - int slash = url.indexOf('/'); - if (slash == -1) { - url = PREFIX_RESOURCE_REF + RESOURCE_CLZ_ATTR + '/' + remainder; - return hasResourceItem(url); - } - } - } - - if (!url.startsWith(PREFIX_RESOURCE_REF)) { - return false; - } - - assert url.startsWith("@") || url.startsWith("?") : url; - - ensureInitialized(); - - int typeEnd = url.indexOf('/', 1); - if (typeEnd != -1) { - int nameBegin = typeEnd + 1; - - // Skip @ and @+ - int typeBegin = url.startsWith("@+") ? 2 : 1; //$NON-NLS-1$ - - int colon = url.lastIndexOf(':', typeEnd); - if (colon != -1) { - typeBegin = colon + 1; - } - String typeName = url.substring(typeBegin, typeEnd); - ResourceType type = ResourceType.getEnum(typeName); - if (type != null) { - String name = url.substring(nameBegin); - return hasResourceItem(type, name); - } - } - - return false; - } - - /** - * Returns true if this resource repository contains a resource of the given - * name. - * - * @param type the type of resource to look up - * @param name the name of the resource - * @return true if the resource is known - */ - public boolean hasResourceItem(@NonNull ResourceType type, @NonNull String name) { - ensureInitialized(); - - Map<String, ResourceItem> map = mResourceMap.get(type); - - if (map != null) { - - ResourceItem resourceItem = map.get(name); - if (resourceItem != null) { - return true; - } - } - - return false; - } - - /** - * Returns a {@link ResourceItem} matching the given {@link ResourceType} and name. If none - * exist, it creates one. - * - * @param type the resource type - * @param name the name of the resource. - * @return A resource item matching the type and name. - */ - @NonNull - protected ResourceItem getResourceItem(@NonNull ResourceType type, @NonNull String name) { - ensureInitialized(); - - // looking for an existing ResourceItem with this type and name - ResourceItem item = findDeclaredResourceItem(type, name); - - // create one if there isn't one already, or if the existing one is inlined, since - // clearly we need a non inlined one (the inline one is removed too) - if (item == null || item.isDeclaredInline()) { - ResourceItem oldItem = item != null && item.isDeclaredInline() ? item : null; - - item = createResourceItem(name); - - Map<String, ResourceItem> map = mResourceMap.get(type); - - if (map == null) { - if (isFrameworkRepository()) { - // Pick initial size for the maps. Also change the load factor to 1.0 - // to avoid rehashing the whole table when we (as expected) get near - // the known rough size of each resource type map. - int size; - switch (type) { - // Based on counts in API 16. Going back to API 10, the counts - // are roughly 25-50% smaller (e.g. compared to the top 5 types below - // the fractions are 1107 vs 1734, 831 vs 1508, 895 vs 1255, - // 733 vs 1064 and 171 vs 783. - case PUBLIC: size = 1734; break; - case DRAWABLE: size = 1508; break; - case STRING: size = 1255; break; - case ATTR: size = 1064; break; - case STYLE: size = 783; break; - case ID: size = 347; break; - case DECLARE_STYLEABLE: size = 210; break; - case LAYOUT: size = 187; break; - case COLOR: size = 120; break; - case ANIM: size = 95; break; - case DIMEN: size = 81; break; - case BOOL: size = 54; break; - case INTEGER: size = 52; break; - case ARRAY: size = 51; break; - case PLURALS: size = 20; break; - case XML: size = 14; break; - case INTERPOLATOR : size = 13; break; - case ANIMATOR: size = 8; break; - case RAW: size = 4; break; - case MENU: size = 2; break; - case MIPMAP: size = 2; break; - case FRACTION: size = 1; break; - default: - size = 2; - } - map = new HashMap<String, ResourceItem>(size, 1.0f); - } else { - map = new HashMap<String, ResourceItem>(); - } - mResourceMap.put(type, map); - } - - map.put(item.getName(), item); - - if (oldItem != null) { - map.remove(oldItem.getName()); - - } - } - - return item; - } - - /** - * Creates a resource item with the given name. - * @param name the name of the resource - * @return a new ResourceItem (or child class) instance. - */ - @NonNull - protected abstract ResourceItem createResourceItem(@NonNull String name); - - /** - * Processes a folder and adds it to the list of existing folders. - * @param folder the folder to process - * @return the ResourceFolder created from this folder, or null if the process failed. - */ - @Nullable - public ResourceFolder processFolder(@NonNull IAbstractFolder folder) { - ensureInitialized(); - - // split the name of the folder in segments. - String[] folderSegments = folder.getName().split(SdkConstants.RES_QUALIFIER_SEP); - - // get the enum for the resource type. - ResourceFolderType type = ResourceFolderType.getTypeByName(folderSegments[0]); - - if (type != null) { - // get the folder configuration. - FolderConfiguration config = FolderConfiguration.getConfig(folderSegments); - - if (config != null) { - return add(type, config, folder); - } - } - - return null; - } - - /** - * Returns a list of {@link ResourceFolder} for a specific {@link ResourceFolderType}. - * @param type The {@link ResourceFolderType} - */ - @Nullable - public List<ResourceFolder> getFolders(@NonNull ResourceFolderType type) { - ensureInitialized(); - - return mFolderMap.get(type); - } - - @NonNull - public List<ResourceType> getAvailableResourceTypes() { - ensureInitialized(); - - List<ResourceType> list = new ArrayList<ResourceType>(); - - // For each key, we check if there's a single ResourceType match. - // If not, we look for the actual content to give us the resource type. - - for (ResourceFolderType folderType : mFolderMap.keySet()) { - List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(folderType); - if (types.size() == 1) { - // before we add it we check if it's not already present, since a ResourceType - // could be created from multiple folders, even for the folders that only create - // one type of resource (drawable for instance, can be created from drawable/ and - // values/) - if (list.contains(types.get(0)) == false) { - list.add(types.get(0)); - } - } else { - // there isn't a single resource type out of this folder, so we look for all - // content. - List<ResourceFolder> folders = mFolderMap.get(folderType); - if (folders != null) { - for (ResourceFolder folder : folders) { - Collection<ResourceType> folderContent = folder.getResourceTypes(); - - // then we add them, but only if they aren't already in the list. - for (ResourceType folderResType : folderContent) { - if (list.contains(folderResType) == false) { - list.add(folderResType); - } - } - } - } - } - } - - return list; - } - - /** - * Returns a list of {@link ResourceItem} matching a given {@link ResourceType}. - * @param type the type of the resource items to return - * @return a non null collection of resource items - */ - @NonNull - public Collection<ResourceItem> getResourceItemsOfType(@NonNull ResourceType type) { - ensureInitialized(); - - Map<String, ResourceItem> map = mResourceMap.get(type); - - if (map == null) { - return Collections.emptyList(); - } - - Collection<ResourceItem> roList = mReadOnlyListMap.get(map); - if (roList == null) { - roList = Collections.unmodifiableCollection(map.values()); - mReadOnlyListMap.put(map, roList); - } - - return roList; - } - - /** - * Returns whether the repository has resources of a given {@link ResourceType}. - * @param type the type of resource to check. - * @return true if the repository contains resources of the given type, false otherwise. - */ - public boolean hasResourcesOfType(@NonNull ResourceType type) { - ensureInitialized(); - - Map<String, ResourceItem> items = mResourceMap.get(type); - return (items != null && items.size() > 0); - } - - /** - * Returns the {@link ResourceFolder} associated with a {@link IAbstractFolder}. - * @param folder The {@link IAbstractFolder} object. - * @return the {@link ResourceFolder} or null if it was not found. - */ - @Nullable - public ResourceFolder getResourceFolder(@NonNull IAbstractFolder folder) { - ensureInitialized(); - - Collection<List<ResourceFolder>> values = mFolderMap.values(); - - for (List<ResourceFolder> list : values) { - for (ResourceFolder resFolder : list) { - IAbstractFolder wrapper = resFolder.getFolder(); - if (wrapper.equals(folder)) { - return resFolder; - } - } - } - - return null; - } - - /** - * Returns the {@link ResourceFile} matching the given name, - * {@link ResourceFolderType} and configuration. - * <p/> - * This only works with files generating one resource named after the file - * (for instance, layouts, bitmap based drawable, xml, anims). - * - * @param name the resource name or file name - * @param type the folder type search for - * @param config the folder configuration to match for - * @return the matching file or <code>null</code> if no match was found. - */ - @Nullable - public ResourceFile getMatchingFile( - @NonNull String name, - @NonNull ResourceFolderType type, - @NonNull FolderConfiguration config) { - List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(type); - for (ResourceType t : types) { - if (t == ResourceType.ID) { - continue; - } - ResourceFile match = getMatchingFile(name, type, config); - if (match != null) { - return match; - } - } - - return null; - } - - /** - * Returns the {@link ResourceFile} matching the given name, - * {@link ResourceType} and configuration. - * <p/> - * This only works with files generating one resource named after the file - * (for instance, layouts, bitmap based drawable, xml, anims). - * - * @param name the resource name or file name - * @param type the folder type search for - * @param config the folder configuration to match for - * @return the matching file or <code>null</code> if no match was found. - */ - @Nullable - public ResourceFile getMatchingFile( - @NonNull String name, - @NonNull ResourceType type, - @NonNull FolderConfiguration config) { - ensureInitialized(); - - String resourceName = name; - int dot = resourceName.indexOf('.'); - if (dot != -1) { - resourceName = resourceName.substring(0, dot); - } - - Map<String, ResourceItem> items = mResourceMap.get(type); - if (items != null) { - ResourceItem item = items.get(resourceName); - if (item != null) { - List<ResourceFile> files = item.getSourceFileList(); - if (files != null) { - if (files.size() > 1) { - ResourceValue value = item.getResourceValue(type, config, - isFrameworkRepository()); - if (value != null) { - String v = value.getValue(); - if (v != null) { - Pair<ResourceType, String> pair = parseResource(v); - if (pair != null) { - return getMatchingFile(pair.getSecond(), pair.getFirst(), - config); - } else { - // Looks like the resource value is pointing to a file - // It's most likely one of the source files for this - // resource item, so check those first - for (ResourceFile f : files) { - if (v.equals(f.getFile().getOsLocation())) { - // Found the file - return f; - } - } - - // No; look up the resource file from the full path - File file = new File(v); - if (file.exists()) { - ResourceFile f = findResourceFile(file); - if (f != null) { - return f; - } - } - } - } - } - } else if (files.size() == 1) { - // Single file: see if it matches - ResourceFile matchingFile = files.get(0); - if (matchingFile.getFolder().getConfiguration().isMatchFor(config)) { - return matchingFile; - } - } - } - } - } - - return null; - } - - @Nullable - private ResourceFile findResourceFile(@NonNull File file) { - // Look up the right resource file for this path - String parentName = file.getParentFile().getName(); - IAbstractFolder folder = getResFolder().getFolder(parentName); - if (folder != null) { - ResourceFolder resourceFolder = getResourceFolder(folder); - if (resourceFolder != null) { - ResourceFile resourceFile = resourceFolder.getFile(file.getName()); - if (resourceFile != null) { - return resourceFile; - } - } - } - - return null; - } - - /** - * Returns the list of source files for a given resource. - * Optionally, if a {@link FolderConfiguration} is given, then only the best - * match for this config is returned. - * - * @param type the type of the resource. - * @param name the name of the resource. - * @param referenceConfig an optional config for which only the best match will be returned. - * - * @return a list of files generating this resource or null if it was not found. - */ - @Nullable - public List<ResourceFile> getSourceFiles(@NonNull ResourceType type, @NonNull String name, - @Nullable FolderConfiguration referenceConfig) { - ensureInitialized(); - - Collection<ResourceItem> items = getResourceItemsOfType(type); - - for (ResourceItem item : items) { - if (name.equals(item.getName())) { - if (referenceConfig != null) { - Configurable match = referenceConfig.findMatchingConfigurable( - item.getSourceFileList()); - - if (match instanceof ResourceFile) { - return Collections.singletonList((ResourceFile) match); - } - - return null; - } - return item.getSourceFileList(); - } - } - - return null; - } - - /** - * Returns the resources values matching a given {@link FolderConfiguration}. - * - * @param referenceConfig the configuration that each value must match. - * @return a map with guaranteed to contain an entry for each {@link ResourceType} - */ - @NonNull - public Map<ResourceType, Map<String, ResourceValue>> getConfiguredResources( - @NonNull FolderConfiguration referenceConfig) { - ensureInitialized(); - - return doGetConfiguredResources(referenceConfig); - } - - /** - * Returns the resources values matching a given {@link FolderConfiguration} for the current - * project. - * - * @param referenceConfig the configuration that each value must match. - * @return a map with guaranteed to contain an entry for each {@link ResourceType} - */ - @NonNull - protected final Map<ResourceType, Map<String, ResourceValue>> doGetConfiguredResources( - @NonNull FolderConfiguration referenceConfig) { - ensureInitialized(); - - Map<ResourceType, Map<String, ResourceValue>> map = - new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class); - - for (ResourceType key : ResourceType.values()) { - // get the local results and put them in the map - map.put(key, getConfiguredResource(key, referenceConfig)); - } - - return map; - } - - /** - * Returns the sorted list of languages used in the resources. - */ - @NonNull - public SortedSet<String> getLanguages() { - ensureInitialized(); - - SortedSet<String> set = new TreeSet<String>(); - - Collection<List<ResourceFolder>> folderList = mFolderMap.values(); - for (List<ResourceFolder> folderSubList : folderList) { - for (ResourceFolder folder : folderSubList) { - FolderConfiguration config = folder.getConfiguration(); - LanguageQualifier lang = config.getLanguageQualifier(); - if (lang != null) { - set.add(lang.getShortDisplayValue()); - } - } - } - - return set; - } - - /** - * Returns the sorted list of regions used in the resources with the given language. - * @param currentLanguage the current language the region must be associated with. - */ - @NonNull - public SortedSet<String> getRegions(@NonNull String currentLanguage) { - ensureInitialized(); - - SortedSet<String> set = new TreeSet<String>(); - - Collection<List<ResourceFolder>> folderList = mFolderMap.values(); - for (List<ResourceFolder> folderSubList : folderList) { - for (ResourceFolder folder : folderSubList) { - FolderConfiguration config = folder.getConfiguration(); - - // get the language - LanguageQualifier lang = config.getLanguageQualifier(); - if (lang != null && lang.getShortDisplayValue().equals(currentLanguage)) { - RegionQualifier region = config.getRegionQualifier(); - if (region != null) { - set.add(region.getShortDisplayValue()); - } - } - } - } - - return set; - } - - /** - * Loads the resources. - */ - public void loadResources() { - clear(); - ensureInitialized(); - } - - protected void removeFile(@NonNull Collection<ResourceType> types, - @NonNull ResourceFile file) { - ensureInitialized(); - - for (ResourceType type : types) { - removeFile(type, file); - } - } - - protected void removeFile(@NonNull ResourceType type, @NonNull ResourceFile file) { - Map<String, ResourceItem> map = mResourceMap.get(type); - if (map != null) { - Collection<ResourceItem> values = map.values(); - for (ResourceItem item : values) { - item.removeFile(file); - } - } - } - - /** - * Returns a map of (resource name, resource value) for the given {@link ResourceType}. - * <p/>The values returned are taken from the resource files best matching a given - * {@link FolderConfiguration}. - * @param type the type of the resources. - * @param referenceConfig the configuration to best match. - */ - @NonNull - private Map<String, ResourceValue> getConfiguredResource(@NonNull ResourceType type, - @NonNull FolderConfiguration referenceConfig) { - - // get the resource item for the given type - Map<String, ResourceItem> items = mResourceMap.get(type); - if (items == null) { - return new HashMap<String, ResourceValue>(); - } - - // create the map - HashMap<String, ResourceValue> map = new HashMap<String, ResourceValue>(items.size()); - - for (ResourceItem item : items.values()) { - ResourceValue value = item.getResourceValue(type, referenceConfig, - isFrameworkRepository()); - if (value != null) { - map.put(item.getName(), value); - } - } - - return map; - } - - - /** - * Cleans up the repository of resource items that have no source file anymore. - */ - public void postUpdateCleanUp() { - // Since removed files/folders remove source files from existing ResourceItem, loop through - // all resource items and remove the ones that have no source files. - - Collection<Map<String, ResourceItem>> maps = mResourceMap.values(); - for (Map<String, ResourceItem> map : maps) { - Set<String> keySet = map.keySet(); - Iterator<String> iterator = keySet.iterator(); - while (iterator.hasNext()) { - String name = iterator.next(); - ResourceItem resourceItem = map.get(name); - if (resourceItem.hasNoSourceFile()) { - iterator.remove(); - } - } - } - } - - /** - * Looks up an existing {@link ResourceItem} by {@link ResourceType} and name. This - * ignores inline resources. - * @param type the Resource Type. - * @param name the Resource name. - * @return the existing ResourceItem or null if no match was found. - */ - @Nullable - private ResourceItem findDeclaredResourceItem(@NonNull ResourceType type, - @NonNull String name) { - Map<String, ResourceItem> map = mResourceMap.get(type); - - if (map != null) { - ResourceItem resourceItem = map.get(name); - if (resourceItem != null && !resourceItem.isDeclaredInline()) { - return resourceItem; - } - } - - return null; - } - - /** - * Return the resource type of the given url, and the resource name - * - * @param url the resource url to be parsed - * @return a pair of the resource type and the resource name - */ - public static Pair<ResourceType,String> parseResource(String url) { - // Handle theme references - if (url.startsWith(PREFIX_THEME_REF)) { - String remainder = url.substring(PREFIX_THEME_REF.length()); - if (url.startsWith(ATTR_REF_PREFIX)) { - url = PREFIX_RESOURCE_REF + url.substring(PREFIX_THEME_REF.length()); - return parseResource(url); - } - int colon = url.indexOf(':'); - if (colon != -1) { - // Convert from ?android:progressBarStyleBig to ?android:attr/progressBarStyleBig - if (remainder.indexOf('/', colon) == -1) { - remainder = remainder.substring(0, colon) + RESOURCE_CLZ_ATTR + '/' - + remainder.substring(colon); - } - url = PREFIX_RESOURCE_REF + remainder; - return parseResource(url); - } else { - int slash = url.indexOf('/'); - if (slash == -1) { - url = PREFIX_RESOURCE_REF + RESOURCE_CLZ_ATTR + '/' + remainder; - return parseResource(url); - } - } - } - - if (!url.startsWith(PREFIX_RESOURCE_REF)) { - return null; - } - int typeEnd = url.indexOf('/', 1); - if (typeEnd == -1) { - return null; - } - int nameBegin = typeEnd + 1; - - // Skip @ and @+ - int typeBegin = url.startsWith("@+") ? 2 : 1; //$NON-NLS-1$ - - int colon = url.lastIndexOf(':', typeEnd); - if (colon != -1) { - typeBegin = colon + 1; - } - String typeName = url.substring(typeBegin, typeEnd); - ResourceType type = ResourceType.getEnum(typeName); - if (type == null) { - return null; - } - String name = url.substring(nameBegin); - - return Pair.of(type, name); - } -} - diff --git a/sdk_common/src/com/android/ide/common/resources/ResourceResolver.java b/sdk_common/src/com/android/ide/common/resources/ResourceResolver.java deleted file mode 100644 index 219c93f..0000000 --- a/sdk_common/src/com/android/ide/common/resources/ResourceResolver.java +++ /dev/null @@ -1,590 +0,0 @@ -/* - * 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.ide.common.resources; - -import static com.android.SdkConstants.ANDROID_PREFIX; -import static com.android.SdkConstants.ANDROID_THEME_PREFIX; -import static com.android.SdkConstants.PREFIX_ANDROID; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; -import static com.android.SdkConstants.PREFIX_THEME_REF; -import static com.android.SdkConstants.REFERENCE_STYLE; - -import com.android.ide.common.rendering.api.LayoutLog; -import com.android.ide.common.rendering.api.RenderResources; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.rendering.api.StyleResourceValue; -import com.android.resources.ResourceType; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -public class ResourceResolver extends RenderResources { - - private final Map<ResourceType, Map<String, ResourceValue>> mProjectResources; - private final Map<ResourceType, Map<String, ResourceValue>> mFrameworkResources; - - private final Map<StyleResourceValue, StyleResourceValue> mStyleInheritanceMap = - new HashMap<StyleResourceValue, StyleResourceValue>(); - - private StyleResourceValue mTheme; - - private FrameworkResourceIdProvider mFrameworkProvider; - private LayoutLog mLogger; - private String mThemeName; - private boolean mIsProjectTheme; - - private ResourceResolver( - Map<ResourceType, Map<String, ResourceValue>> projectResources, - Map<ResourceType, Map<String, ResourceValue>> frameworkResources) { - mProjectResources = projectResources; - mFrameworkResources = frameworkResources; - } - - /** - * Creates a new {@link ResourceResolver} object. - * - * @param projectResources the project resources. - * @param frameworkResources the framework resources. - * @param themeName the name of the current theme. - * @param isProjectTheme Is this a project theme? - * @return a new {@link ResourceResolver} - */ - public static ResourceResolver create( - Map<ResourceType, Map<String, ResourceValue>> projectResources, - Map<ResourceType, Map<String, ResourceValue>> frameworkResources, - String themeName, boolean isProjectTheme) { - - ResourceResolver resolver = new ResourceResolver( - projectResources, frameworkResources); - - resolver.computeStyleMaps(themeName, isProjectTheme); - - return resolver; - } - - // ---- Methods to help dealing with older LayoutLibs. - - public String getThemeName() { - return mThemeName; - } - - public boolean isProjectTheme() { - return mIsProjectTheme; - } - - public Map<ResourceType, Map<String, ResourceValue>> getProjectResources() { - return mProjectResources; - } - - public Map<ResourceType, Map<String, ResourceValue>> getFrameworkResources() { - return mFrameworkResources; - } - - // ---- RenderResources Methods - - @Override - public void setFrameworkResourceIdProvider(FrameworkResourceIdProvider provider) { - mFrameworkProvider = provider; - } - - @Override - public void setLogger(LayoutLog logger) { - mLogger = logger; - } - - @Override - public StyleResourceValue getCurrentTheme() { - return mTheme; - } - - @Override - public StyleResourceValue getTheme(String name, boolean frameworkTheme) { - ResourceValue theme = null; - - if (frameworkTheme) { - Map<String, ResourceValue> frameworkStyleMap = mFrameworkResources.get( - ResourceType.STYLE); - theme = frameworkStyleMap.get(name); - } else { - Map<String, ResourceValue> projectStyleMap = mProjectResources.get(ResourceType.STYLE); - theme = projectStyleMap.get(name); - } - - if (theme instanceof StyleResourceValue) { - return (StyleResourceValue) theme; - } - - return null; - } - - @Override - public boolean themeIsParentOf(StyleResourceValue parentTheme, StyleResourceValue childTheme) { - do { - childTheme = mStyleInheritanceMap.get(childTheme); - if (childTheme == null) { - return false; - } else if (childTheme == parentTheme) { - return true; - } - } while (true); - } - - @Override - public ResourceValue getFrameworkResource(ResourceType resourceType, String resourceName) { - return getResource(resourceType, resourceName, mFrameworkResources); - } - - @Override - public ResourceValue getProjectResource(ResourceType resourceType, String resourceName) { - return getResource(resourceType, resourceName, mProjectResources); - } - - @Override - @Deprecated - public ResourceValue findItemInStyle(StyleResourceValue style, String attrName) { - // this method is deprecated because it doesn't know about the namespace of the - // attribute so we search for the project namespace first and then in the - // android namespace if needed. - ResourceValue item = findItemInStyle(style, attrName, false /*isFrameworkAttr*/); - if (item == null) { - item = findItemInStyle(style, attrName, true /*isFrameworkAttr*/); - } - - return item; - } - - @Override - public ResourceValue findItemInStyle(StyleResourceValue style, String itemName, - boolean isFrameworkAttr) { - ResourceValue item = style.findValue(itemName, isFrameworkAttr); - - // if we didn't find it, we look in the parent style (if applicable) - if (item == null && mStyleInheritanceMap != null) { - StyleResourceValue parentStyle = mStyleInheritanceMap.get(style); - if (parentStyle != null) { - return findItemInStyle(parentStyle, itemName, isFrameworkAttr); - } - } - - return item; - } - - @Override - public ResourceValue findResValue(String reference, boolean forceFrameworkOnly) { - if (reference == null) { - return null; - } - if (reference.startsWith(PREFIX_THEME_REF) - && reference.length() > PREFIX_THEME_REF.length()) { - // no theme? no need to go further! - if (mTheme == null) { - return null; - } - - boolean frameworkOnly = false; - - // eliminate the prefix from the string - String originalReference = reference; - if (reference.startsWith(ANDROID_THEME_PREFIX)) { - frameworkOnly = true; - reference = reference.substring(ANDROID_THEME_PREFIX.length()); - } else { - reference = reference.substring(PREFIX_THEME_REF.length()); - } - - // at this point, value can contain type/name (drawable/foo for instance). - // split it to make sure. - String[] segments = reference.split("/"); - - // we look for the referenced item name. - String referenceName = null; - - if (segments.length == 2) { - // there was a resType in the reference. If it's attr, we ignore it - // else, we assert for now. - if (ResourceType.ATTR.getName().equals(segments[0])) { - referenceName = segments[1]; - } else { - // At this time, no support for ?type/name where type is not "attr" - return null; - } - } else { - // it's just an item name. - referenceName = segments[0]; - - // Make sure it looks like a resource name; if not, it could just be a string - // which starts with a ? - if (!Character.isJavaIdentifierStart(referenceName.charAt(0))) { - return null; - } - for (int i = 1, n = referenceName.length(); i < n; i++) { - char c = referenceName.charAt(i); - if (!Character.isJavaIdentifierPart(c) && c != '.') { - return null; - } - } - } - - // now we look for android: in the referenceName in order to support format - // such as: ?attr/android:name - if (referenceName.startsWith(PREFIX_ANDROID)) { - frameworkOnly = true; - referenceName = referenceName.substring(PREFIX_ANDROID.length()); - } - - // Now look for the item in the theme, starting with the current one. - ResourceValue item = findItemInStyle(mTheme, referenceName, - forceFrameworkOnly || frameworkOnly); - - if (item == null && mLogger != null) { - mLogger.warning(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR, - String.format("Couldn't find theme resource %1$s for the current theme", - reference), - new ResourceValue(ResourceType.ATTR, originalReference, frameworkOnly)); - } - - return item; - } else if (reference.startsWith(PREFIX_RESOURCE_REF)) { - boolean frameworkOnly = false; - - // check for the specific null reference value. - if (REFERENCE_NULL.equals(reference)) { - return null; - } - - // Eliminate the prefix from the string. - if (reference.startsWith(ANDROID_PREFIX)) { - frameworkOnly = true; - reference = reference.substring(ANDROID_PREFIX.length()); - } else { - reference = reference.substring(PREFIX_RESOURCE_REF.length()); - } - - // at this point, value contains type/[android:]name (drawable/foo for instance) - String[] segments = reference.split("/"); - if (segments.length != 2) { - return null; - } - - // now we look for android: in the resource name in order to support format - // such as: @drawable/android:name - String referenceName = segments[1]; - if (referenceName.startsWith(PREFIX_ANDROID)) { - frameworkOnly = true; - referenceName = referenceName.substring(PREFIX_ANDROID.length()); - } - - ResourceType type = ResourceType.getEnum(segments[0]); - - // unknown type? - if (type == null) { - return null; - } - - // Make sure it looks like a resource name; if not, it could just be a string - // which starts with a ? - if (!Character.isJavaIdentifierStart(referenceName.charAt(0))) { - return null; - } - for (int i = 1, n = referenceName.length(); i < n; i++) { - char c = referenceName.charAt(i); - if (!Character.isJavaIdentifierPart(c) && c != '.') { - return null; - } - } - - return findResValue(type, referenceName, - forceFrameworkOnly ? true :frameworkOnly); - } - - // Looks like the value didn't reference anything. Return null. - return null; - } - - @Override - public ResourceValue resolveValue(ResourceType type, String name, String value, - boolean isFrameworkValue) { - if (value == null) { - return null; - } - - // get the ResourceValue referenced by this value - ResourceValue resValue = findResValue(value, isFrameworkValue); - - // if resValue is null, but value is not null, this means it was not a reference. - // we return the name/value wrapper in a ResourceValue. the isFramework flag doesn't - // matter. - if (resValue == null) { - return new ResourceValue(type, name, value, isFrameworkValue); - } - - // we resolved a first reference, but we need to make sure this isn't a reference also. - return resolveResValue(resValue); - } - - @Override - public ResourceValue resolveResValue(ResourceValue resValue) { - if (resValue == null) { - return null; - } - - // if the resource value is null, we simply return it. - String value = resValue.getValue(); - if (value == null) { - return resValue; - } - - // else attempt to find another ResourceValue referenced by this one. - ResourceValue resolvedResValue = findResValue(value, resValue.isFramework()); - - // if the value did not reference anything, then we simply return the input value - if (resolvedResValue == null) { - return resValue; - } - - // detect potential loop due to mishandled namespace in attributes - if (resValue == resolvedResValue) { - if (mLogger != null) { - mLogger.error(LayoutLog.TAG_BROKEN, - String.format("Potential stackoverflow trying to resolve '%s'. Render may not be accurate.", value), - null); - } - return resValue; - } - - // otherwise, we attempt to resolve this new value as well - return resolveResValue(resolvedResValue); - } - - // ---- Private helper methods. - - /** - * Searches for, and returns a {@link ResourceValue} by its name, and type. - * @param resType the type of the resource - * @param resName the name of the resource - * @param frameworkOnly if <code>true</code>, the method does not search in the - * project resources - */ - private ResourceValue findResValue(ResourceType resType, String resName, - boolean frameworkOnly) { - // map of ResouceValue for the given type - Map<String, ResourceValue> typeMap; - - // if allowed, search in the project resources first. - if (frameworkOnly == false) { - typeMap = mProjectResources.get(resType); - ResourceValue item = typeMap.get(resName); - if (item != null) { - return item; - } - } - - // now search in the framework resources. - typeMap = mFrameworkResources.get(resType); - ResourceValue item = typeMap.get(resName); - if (item != null) { - return item; - } - - // if it was not found and the type is an id, it is possible that the ID was - // generated dynamically when compiling the framework resources. - // Look for it in the R map. - if (mFrameworkProvider != null && resType == ResourceType.ID) { - if (mFrameworkProvider.getId(resType, resName) != null) { - return new ResourceValue(resType, resName, true); - } - } - - // didn't find the resource anywhere. - if (mLogger != null) { - mLogger.warning(LayoutLog.TAG_RESOURCES_RESOLVE, - "Couldn't resolve resource @" + - (frameworkOnly ? "android:" : "") + resType + "/" + resName, - new ResourceValue(resType, resName, frameworkOnly)); - } - return null; - } - - private ResourceValue getResource(ResourceType resourceType, String resourceName, - Map<ResourceType, Map<String, ResourceValue>> resourceRepository) { - Map<String, ResourceValue> typeMap = resourceRepository.get(resourceType); - if (typeMap != null) { - ResourceValue item = typeMap.get(resourceName); - if (item != null) { - item = resolveResValue(item); - return item; - } - } - - // didn't find the resource anywhere. - return null; - - } - - /** - * Compute style information from the given list of style for the project and framework. - * @param themeName the name of the current theme. - * @param isProjectTheme Is this a project theme? - */ - private void computeStyleMaps(String themeName, boolean isProjectTheme) { - mThemeName = themeName; - mIsProjectTheme = isProjectTheme; - Map<String, ResourceValue> projectStyleMap = mProjectResources.get(ResourceType.STYLE); - Map<String, ResourceValue> frameworkStyleMap = mFrameworkResources.get(ResourceType.STYLE); - - // first, get the theme - ResourceValue theme = null; - - // project theme names have been prepended with a * - if (isProjectTheme) { - theme = projectStyleMap.get(themeName); - } else { - theme = frameworkStyleMap.get(themeName); - } - - if (theme instanceof StyleResourceValue) { - // compute the inheritance map for both the project and framework styles - computeStyleInheritance(projectStyleMap.values(), projectStyleMap, - frameworkStyleMap); - - // Compute the style inheritance for the framework styles/themes. - // Since, for those, the style parent values do not contain 'android:' - // we want to force looking in the framework style only to avoid using - // similarly named styles from the project. - // To do this, we pass null in lieu of the project style map. - computeStyleInheritance(frameworkStyleMap.values(), null /*inProjectStyleMap */, - frameworkStyleMap); - - mTheme = (StyleResourceValue) theme; - } - } - - - - /** - * Compute the parent style for all the styles in a given list. - * @param styles the styles for which we compute the parent. - * @param inProjectStyleMap the map of project styles. - * @param inFrameworkStyleMap the map of framework styles. - * @param outInheritanceMap the map of style inheritance. This is filled by the method. - */ - private void computeStyleInheritance(Collection<ResourceValue> styles, - Map<String, ResourceValue> inProjectStyleMap, - Map<String, ResourceValue> inFrameworkStyleMap) { - for (ResourceValue value : styles) { - if (value instanceof StyleResourceValue) { - StyleResourceValue style = (StyleResourceValue)value; - StyleResourceValue parentStyle = null; - - // first look for a specified parent. - String parentName = style.getParentStyle(); - - // no specified parent? try to infer it from the name of the style. - if (parentName == null) { - parentName = getParentName(value.getName()); - } - - if (parentName != null) { - parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap); - - if (parentStyle != null) { - mStyleInheritanceMap.put(style, parentStyle); - } - } - } - } - } - - - /** - * Computes the name of the parent style, or <code>null</code> if the style is a root style. - */ - private String getParentName(String styleName) { - int index = styleName.lastIndexOf('.'); - if (index != -1) { - return styleName.substring(0, index); - } - - return null; - } - - /** - * Searches for and returns the {@link StyleResourceValue} from a given name. - * <p/>The format of the name can be: - * <ul> - * <li>[android:]<name></li> - * <li>[android:]style/<name></li> - * <li>@[android:]style/<name></li> - * </ul> - * @param parentName the name of the style. - * @param inProjectStyleMap the project style map. Can be <code>null</code> - * @param inFrameworkStyleMap the framework style map. - * @return The matching {@link StyleResourceValue} object or <code>null</code> if not found. - */ - private StyleResourceValue getStyle(String parentName, - Map<String, ResourceValue> inProjectStyleMap, - Map<String, ResourceValue> inFrameworkStyleMap) { - boolean frameworkOnly = false; - - String name = parentName; - - // remove the useless @ if it's there - if (name.startsWith(PREFIX_RESOURCE_REF)) { - name = name.substring(PREFIX_RESOURCE_REF.length()); - } - - // check for framework identifier. - if (name.startsWith(PREFIX_ANDROID)) { - frameworkOnly = true; - name = name.substring(PREFIX_ANDROID.length()); - } - - // at this point we could have the format <type>/<name>. we want only the name as long as - // the type is style. - if (name.startsWith(REFERENCE_STYLE)) { - name = name.substring(REFERENCE_STYLE.length()); - } else if (name.indexOf('/') != -1) { - return null; - } - - ResourceValue parent = null; - - // if allowed, search in the project resources. - if (frameworkOnly == false && inProjectStyleMap != null) { - parent = inProjectStyleMap.get(name); - } - - // if not found, then look in the framework resources. - if (parent == null) { - parent = inFrameworkStyleMap.get(name); - } - - // make sure the result is the proper class type and return it. - if (parent instanceof StyleResourceValue) { - return (StyleResourceValue)parent; - } - - if (mLogger != null) { - mLogger.error(LayoutLog.TAG_RESOURCES_RESOLVE, - String.format("Unable to resolve parent style name: %s", parentName), - null /*data*/); - } - - return null; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/ScanningContext.java b/sdk_common/src/com/android/ide/common/resources/ScanningContext.java deleted file mode 100644 index 43561e8..0000000 --- a/sdk_common/src/com/android/ide/common/resources/ScanningContext.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.common.resources; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; - -/** - * A {@link ScanningContext} keeps track of state during a resource file scan, - * such as any parsing errors encountered, whether Android ids have changed, and - * so on. - */ -public class ScanningContext { - protected final ResourceRepository mRepository; - private boolean mNeedsFullAapt; - private List<String> mErrors = null; - - /** - * Constructs a new {@link ScanningContext} - * - * @param repository the associated resource repository - */ - public ScanningContext(@NonNull ResourceRepository repository) { - super(); - mRepository = repository; - } - - /** - * Returns a list of errors encountered during scanning - * - * @return a list of errors encountered during scanning (or null) - */ - @Nullable - public List<String> getErrors() { - return mErrors; - } - - /** - * Adds the given error to the scanning context. The error should use the - * same syntax as real aapt error messages such that the aapt parser can - * properly detect the filename, line number, etc. - * - * @param error the error message, including file name and line number at - * the beginning - */ - public void addError(@NonNull String error) { - if (mErrors == null) { - mErrors = new ArrayList<String>(); - } - mErrors.add(error); - } - - /** - * Returns the repository associated with this scanning context - * - * @return the associated repository, never null - */ - @NonNull - public ResourceRepository getRepository() { - return mRepository; - } - - /** - * Marks that a full aapt compilation of the resources is necessary because it has - * detected a change that cannot be incrementally handled. - */ - protected void requestFullAapt() { - mNeedsFullAapt = true; - } - - /** - * Returns whether this repository has been marked as "dirty"; if one or - * more of the constituent files have declared that the resource item names - * that they provide have changed. - * - * @return true if a full aapt compilation is required - */ - public boolean needsFullAapt() { - return mNeedsFullAapt; - } - - /** - * Asks the context to check whether the given attribute name and value is valid - * in this context. - * - * @param uri the XML namespace URI - * @param name the attribute local name - * @param value the attribute value - * @return true if the attribute is valid - */ - public boolean checkValue(@Nullable String uri, @NonNull String name, @NonNull String value) { - return true; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/SingleResourceFile.java b/sdk_common/src/com/android/ide/common/resources/SingleResourceFile.java deleted file mode 100644 index 8e394d3..0000000 --- a/sdk_common/src/com/android/ide/common/resources/SingleResourceFile.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources; - -import static com.android.SdkConstants.DOT_XML; - -import com.android.ide.common.rendering.api.DensityBasedResourceValue; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.configuration.DensityQualifier; -import com.android.io.IAbstractFile; -import com.android.resources.FolderTypeRelationship; -import com.android.resources.ResourceType; -import com.android.utils.SdkUtils; - -import java.util.Collection; -import java.util.List; - -import javax.xml.parsers.SAXParserFactory; - -/** - * Represents a resource file describing a single resource. - * <p/> - * This is typically an XML file inside res/anim, res/layout, or res/menu or an image file - * under res/drawable. - */ -public class SingleResourceFile extends ResourceFile { - - private final static SAXParserFactory sParserFactory = SAXParserFactory.newInstance(); - static { - sParserFactory.setNamespaceAware(true); - } - - private final String mResourceName; - private final ResourceType mType; - private ResourceValue mValue; - - public SingleResourceFile(IAbstractFile file, ResourceFolder folder) { - super(file, folder); - - // we need to infer the type of the resource from the folder type. - // This is easy since this is a single Resource file. - List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(folder.getType()); - mType = types.get(0); - - // compute the resource name - mResourceName = getResourceName(mType); - - // test if there's a density qualifier associated with the resource - DensityQualifier qualifier = folder.getConfiguration().getDensityQualifier(); - - if (qualifier == null) { - mValue = new ResourceValue(mType, getResourceName(mType), - file.getOsLocation(), isFramework()); - } else { - mValue = new DensityBasedResourceValue( - mType, - getResourceName(mType), - file.getOsLocation(), - qualifier.getValue(), - isFramework()); - } - } - - @Override - protected void load(ScanningContext context) { - // get a resource item matching the given type and name - ResourceItem item = getRepository().getResourceItem(mType, mResourceName); - - // add this file to the list of files generating this resource item. - item.add(this); - - // Ask for an ID refresh since we're adding an item that will generate an ID - context.requestFullAapt(); - } - - @Override - protected void update(ScanningContext context) { - // when this happens, nothing needs to be done since the file only generates - // a single resources that doesn't actually change (its content is the file path) - - // However, we should check for newly introduced errors - // Parse the file and look for @+id/ entries - validateAttributes(context); - } - - @Override - protected void dispose(ScanningContext context) { - // only remove this file from the existing ResourceItem. - getFolder().getRepository().removeFile(mType, this); - - // Ask for an ID refresh since we're removing an item that previously generated an ID - context.requestFullAapt(); - - // don't need to touch the content, it'll get reclaimed as this objects disappear. - // In the mean time other objects may need to access it. - } - - @Override - public Collection<ResourceType> getResourceTypes() { - return FolderTypeRelationship.getRelatedResourceTypes(getFolder().getType()); - } - - @Override - public boolean hasResources(ResourceType type) { - return FolderTypeRelationship.match(type, getFolder().getType()); - } - - /* - * (non-Javadoc) - * @see com.android.ide.eclipse.editors.resources.manager.ResourceFile#getValue(com.android.ide.eclipse.common.resources.ResourceType, java.lang.String) - * - * This particular implementation does not care about the type or name since a - * SingleResourceFile represents a file generating only one resource. - * The value returned is the full absolute path of the file in OS form. - */ - @Override - public ResourceValue getValue(ResourceType type, String name) { - return mValue; - } - - /** - * Returns the name of the resources. - */ - private String getResourceName(ResourceType type) { - // get the name from the filename. - String name = getFile().getName(); - - int pos = name.indexOf('.'); - if (pos != -1) { - name = name.substring(0, pos); - } - - return name; - } - - /** - * Validates the associated resource file to make sure the attribute references are valid - * - * @return true if parsing succeeds and false if it fails - */ - private boolean validateAttributes(ScanningContext context) { - // We only need to check if it's a non-framework file (and an XML file; skip .png's) - if (!isFramework() && SdkUtils.endsWith(getFile().getName(), DOT_XML)) { - ValidatingResourceParser parser = new ValidatingResourceParser(context, false); - try { - IAbstractFile file = getFile(); - return parser.parse(file.getOsLocation(), file.getContents()); - } catch (Exception e) { - context.needsFullAapt(); - } - - return false; - } - - return true; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/ValidatingResourceParser.java b/sdk_common/src/com/android/ide/common/resources/ValidatingResourceParser.java deleted file mode 100644 index b477b8f..0000000 --- a/sdk_common/src/com/android/ide/common/resources/ValidatingResourceParser.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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.ide.common.resources; - -import com.android.annotations.NonNull; -import com.google.common.io.Closeables; - -import org.kxml2.io.KXmlParser; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * Parser for scanning an XML resource file and validating all framework - * attribute references in it. If an error is found, the associated context - * is marked as needing a full AAPT run. - */ -public class ValidatingResourceParser { - private final boolean mIsFramework; - private ScanningContext mContext; - - /** - * Creates a new {@link ValidatingResourceParser} - * - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - * @param isFramework true if scanning a framework resource - */ - public ValidatingResourceParser( - @NonNull ScanningContext context, - boolean isFramework) { - mContext = context; - mIsFramework = isFramework; - } - - /** - * Parse the given input and return false if it contains errors, <b>or</b> if - * the context is already tagged as needing a full aapt run. - * - * @param path the full OS path to the file being parsed - * @param input the input stream of the XML to be parsed (will be closed by this method) - * @return true if parsing succeeds and false if it fails - * @throws IOException if reading the contents fails - */ - public boolean parse(final String path, InputStream input) - throws IOException { - // No need to validate framework files - if (mIsFramework) { - Closeables.closeQuietly(input); - return true; - } - if (mContext.needsFullAapt()) { - Closeables.closeQuietly(input); - return false; - } - - KXmlParser parser = new KXmlParser(); - try { - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - - if (input instanceof FileInputStream) { - input = new BufferedInputStream(input); - } - parser.setInput(input, "UTF-8"); //$NON-NLS-1$ - - return parse(path, parser); - } catch (XmlPullParserException e) { - String message = e.getMessage(); - - // Strip off position description - int index = message.indexOf("(position:"); //$NON-NLS-1$ (Hardcoded in KXml) - if (index != -1) { - message = message.substring(0, index); - } - - String error = String.format("%1$s:%2$d: Error: %3$s", //$NON-NLS-1$ - path, parser.getLineNumber(), message); - mContext.addError(error); - return false; - } catch (RuntimeException e) { - // Some exceptions are thrown by the KXmlParser that are not XmlPullParserExceptions, - // such as this one: - // java.lang.RuntimeException: Undefined Prefix: w in org.kxml2.io.KXmlParser@... - // at org.kxml2.io.KXmlParser.adjustNsp(Unknown Source) - // at org.kxml2.io.KXmlParser.parseStartTag(Unknown Source) - String message = e.getMessage(); - String error = String.format("%1$s:%2$d: Error: %3$s", //$NON-NLS-1$ - path, parser.getLineNumber(), message); - mContext.addError(error); - return false; - } finally { - Closeables.closeQuietly(input); - } - } - - private boolean parse(String path, KXmlParser parser) - throws XmlPullParserException, IOException { - boolean checkForErrors = !mIsFramework && !mContext.needsFullAapt(); - - while (true) { - int event = parser.next(); - if (event == XmlPullParser.START_TAG) { - for (int i = 0, n = parser.getAttributeCount(); i < n; i++) { - String attribute = parser.getAttributeName(i); - String value = parser.getAttributeValue(i); - assert value != null : attribute; - - if (checkForErrors) { - String uri = parser.getAttributeNamespace(i); - if (!mContext.checkValue(uri, attribute, value)) { - mContext.requestFullAapt(); - return false; - } - } - } - } else if (event == XmlPullParser.END_DOCUMENT) { - break; - } - } - - return true; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/ValueResourceParser.java b/sdk_common/src/com/android/ide/common/resources/ValueResourceParser.java deleted file mode 100644 index aa1f4b8..0000000 --- a/sdk_common/src/com/android/ide/common/resources/ValueResourceParser.java +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.resources; - -import static com.android.SdkConstants.AMP_ENTITY; -import static com.android.SdkConstants.LT_ENTITY; - -import com.android.annotations.Nullable; -import com.android.annotations.VisibleForTesting; -import com.android.ide.common.rendering.api.AttrResourceValue; -import com.android.ide.common.rendering.api.DeclareStyleableResourceValue; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.rendering.api.StyleResourceValue; -import com.android.resources.ResourceType; - -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -/** - * SAX handler to parser value resource files. - */ -public final class ValueResourceParser extends DefaultHandler { - - // TODO: reuse definitions from somewhere else. - private final static String NODE_RESOURCES = "resources"; - private final static String NODE_ITEM = "item"; - private final static String ATTR_NAME = "name"; - private final static String ATTR_TYPE = "type"; - private final static String ATTR_PARENT = "parent"; - private final static String ATTR_VALUE = "value"; - - private final static String DEFAULT_NS_PREFIX = "android:"; - private final static int DEFAULT_NS_PREFIX_LEN = DEFAULT_NS_PREFIX.length(); - - public interface IValueResourceRepository { - void addResourceValue(ResourceValue value); - boolean hasResourceValue(ResourceType type, String name); - } - - private boolean inResources = false; - private int mDepth = 0; - private ResourceValue mCurrentValue = null; - private StyleResourceValue mCurrentStyle = null; - private DeclareStyleableResourceValue mCurrentDeclareStyleable = null; - private AttrResourceValue mCurrentAttr; - private IValueResourceRepository mRepository; - private final boolean mIsFramework; - - public ValueResourceParser(IValueResourceRepository repository, boolean isFramework) { - mRepository = repository; - mIsFramework = isFramework; - } - - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - if (mCurrentValue != null) { - mCurrentValue.setValue(unescapeResourceString(mCurrentValue.getValue(), false, true)); - } - - if (inResources && qName.equals(NODE_RESOURCES)) { - inResources = false; - } else if (mDepth == 2) { - mCurrentValue = null; - mCurrentStyle = null; - mCurrentDeclareStyleable = null; - mCurrentAttr = null; - } else if (mDepth == 3) { - mCurrentValue = null; - if (mCurrentDeclareStyleable != null) { - mCurrentAttr = null; - } - } - - mDepth--; - super.endElement(uri, localName, qName); - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) - throws SAXException { - try { - mDepth++; - if (inResources == false && mDepth == 1) { - if (qName.equals(NODE_RESOURCES)) { - inResources = true; - } - } else if (mDepth == 2 && inResources == true) { - ResourceType type = getType(qName, attributes); - - if (type != null) { - // get the resource name - String name = attributes.getValue(ATTR_NAME); - if (name != null) { - switch (type) { - case STYLE: - String parent = attributes.getValue(ATTR_PARENT); - mCurrentStyle = new StyleResourceValue(type, name, parent, - mIsFramework); - mRepository.addResourceValue(mCurrentStyle); - break; - case DECLARE_STYLEABLE: - mCurrentDeclareStyleable = new DeclareStyleableResourceValue( - type, name, mIsFramework); - mRepository.addResourceValue(mCurrentDeclareStyleable); - break; - case ATTR: - mCurrentAttr = new AttrResourceValue(type, name, mIsFramework); - mRepository.addResourceValue(mCurrentAttr); - break; - default: - mCurrentValue = new ResourceValue(type, name, mIsFramework); - mRepository.addResourceValue(mCurrentValue); - break; - } - } - } - } else if (mDepth == 3) { - // get the resource name - String name = attributes.getValue(ATTR_NAME); - if (name != null) { - - if (mCurrentStyle != null) { - // is the attribute in the android namespace? - boolean isFrameworkAttr = mIsFramework; - if (name.startsWith(DEFAULT_NS_PREFIX)) { - name = name.substring(DEFAULT_NS_PREFIX_LEN); - isFrameworkAttr = true; - } - - mCurrentValue = new ResourceValue(null, name, mIsFramework); - mCurrentStyle.addValue(mCurrentValue, isFrameworkAttr); - } else if (mCurrentDeclareStyleable != null) { - // is the attribute in the android namespace? - boolean isFramework = mIsFramework; - if (name.startsWith(DEFAULT_NS_PREFIX)) { - name = name.substring(DEFAULT_NS_PREFIX_LEN); - isFramework = true; - } - - mCurrentAttr = new AttrResourceValue(ResourceType.ATTR, name, isFramework); - mCurrentDeclareStyleable.addValue(mCurrentAttr); - - // also add it to the repository. - mRepository.addResourceValue(mCurrentAttr); - - } else if (mCurrentAttr != null) { - // get the enum/flag value - String value = attributes.getValue(ATTR_VALUE); - - try { - // Integer.decode/parseInt can't deal with hex value > 0x7FFFFFFF so we - // use Long.decode instead. - mCurrentAttr.addValue(name, (int)(long)Long.decode(value)); - } catch (NumberFormatException e) { - // pass, we'll just ignore this value - } - - } - } - } else if (mDepth == 4 && mCurrentAttr != null) { - // get the enum/flag name - String name = attributes.getValue(ATTR_NAME); - String value = attributes.getValue(ATTR_VALUE); - - try { - // Integer.decode/parseInt can't deal with hex value > 0x7FFFFFFF so we - // use Long.decode instead. - mCurrentAttr.addValue(name, (int)(long)Long.decode(value)); - } catch (NumberFormatException e) { - // pass, we'll just ignore this value - } - } - } finally { - super.startElement(uri, localName, qName, attributes); - } - } - - private ResourceType getType(String qName, Attributes attributes) { - String typeValue; - - // if the node is <item>, we get the type from the attribute "type" - if (NODE_ITEM.equals(qName)) { - typeValue = attributes.getValue(ATTR_TYPE); - } else { - // the type is the name of the node. - typeValue = qName; - } - - ResourceType type = ResourceType.getEnum(typeValue); - return type; - } - - - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - if (mCurrentValue != null) { - String value = mCurrentValue.getValue(); - if (value == null) { - mCurrentValue.setValue(new String(ch, start, length)); - } else { - mCurrentValue.setValue(value + new String(ch, start, length)); - } - } - } - - /** - * Replaces escapes in an XML resource string with the actual characters, - * performing unicode substitutions (replacing any {@code \\uNNNN} references in the - * given string with the corresponding unicode characters), etc. - * - * - * - * @param s the string to unescape - * @param escapeEntities XML entities - * @param trim whether surrounding space and quotes should be trimmed - * @return the string with the escape characters removed and expanded - */ - @Nullable - public static String unescapeResourceString( - @Nullable String s, - boolean escapeEntities, boolean trim) { - if (s == null) { - return null; - } - - // Trim space surrounding optional quotes - int i = 0; - int n = s.length(); - if (trim) { - while (i < n) { - char c = s.charAt(i); - if (!Character.isWhitespace(c)) { - break; - } - i++; - } - while (n > i) { - char c = s.charAt(n - 1); - if (!Character.isWhitespace(c)) { - //See if this was a \, and if so, see whether it was escaped - if (n < s.length() && isEscaped(s, n)) { - n++; - } - break; - } - n--; - } - - // Trim surrounding quotes. Note that there can be *any* number of these, and - // the left side and right side do not have to match; e.g. you can have - // """"f"" => f - int quoteStart = i; - int quoteEnd = n; - while (i < n) { - char c = s.charAt(i); - if (c != '"') { - break; - } - i++; - } - // Searching backwards is slightly more complicated; make sure we don't trim - // quotes that have been escaped. - while (n > i) { - char c = s.charAt(n - 1); - if (c != '"') { - if (n < s.length() && isEscaped(s, n)) { - n++; - } - break; - } - n--; - } - if (n == i) { - return ""; //$NON-NLS-1$ - } - - // Only trim leading spaces if we didn't already process a leading quote: - if (i == quoteStart) { - while (i < n) { - char c = s.charAt(i); - if (!Character.isWhitespace(c)) { - break; - } - i++; - } - } - // Only trim trailing spaces if we didn't already process a trailing quote: - if (n == quoteEnd) { - while (n > i) { - char c = s.charAt(n - 1); - if (!Character.isWhitespace(c)) { - //See if this was a \, and if so, see whether it was escaped - if (n < s.length() && isEscaped(s, n)) { - n++; - } - break; - } - n--; - } - } - if (n == i) { - return ""; //$NON-NLS-1$ - } - } - - // If no surrounding whitespace and no escape characters, no need to do any - // more work - if (i == 0 && n == s.length() && s.indexOf('\\') == -1 - && (!escapeEntities || s.indexOf('&') == -1)) { - return s; - } - - StringBuilder sb = new StringBuilder(n - i); - for (; i < n; i++) { - char c = s.charAt(i); - if (c == '\\' && i < n - 1) { - char next = s.charAt(i + 1); - // Unicode escapes - if (next == 'u' && i < n - 5) { // case sensitive - String hex = s.substring(i + 2, i + 6); - try { - int unicodeValue = Integer.parseInt(hex, 16); - sb.append((char) unicodeValue); - i += 5; - continue; - } catch (NumberFormatException e) { - // Invalid escape: Just proceed to literally transcribe it - sb.append(c); - } - } else if (next == 'n') { - sb.append('\n'); - i++; - } else if (next == 't') { - sb.append('\t'); - i++; - } else { - sb.append(next); - i++; - continue; - } - } else { - if (c == '&' && escapeEntities) { - if (s.regionMatches(true, i, LT_ENTITY, 0, LT_ENTITY.length())) { - sb.append('<'); - i += LT_ENTITY.length() - 1; - continue; - } else if (s.regionMatches(true, i, AMP_ENTITY, 0, AMP_ENTITY.length())) { - sb.append('&'); - i += AMP_ENTITY.length() - 1; - continue; - } - } - sb.append(c); - } - } - s = sb.toString(); - - return s; - } - - /** - * Returns true if the character at the given offset in the string is escaped - * (the previous character is a \, and that character isn't itself an escaped \) - * - * @param s the string - * @param index the index of the character in the string to check - * @return true if the character is escaped - */ - @VisibleForTesting - static boolean isEscaped(String s, int index) { - if (index == 0 || index == s.length()) { - return false; - } - int prevPos = index - 1; - char prev = s.charAt(prevPos); - if (prev != '\\') { - return false; - } - // The character *may* be escaped; not sure if the \ we ran into is - // an escape character, or an escaped backslash; we have to search backwards - // to be certain. - int j = prevPos - 1; - while (j >= 0) { - if (s.charAt(j) != '\\') { - break; - } - j--; - } - // If we passed an odd number of \'s, the space is escaped - return (prevPos - j) % 2 == 1; - } - - /** - * Escape a string value to be placed in a string resource file such that it complies with - * the escaping rules described here: - * http://developer.android.com/guide/topics/resources/string-resource.html - * More examples of the escaping rules can be found here: - * http://androidcookbook.com/Recipe.seam?recipeId=2219&recipeFrom=ViewTOC - * This method assumes that the String is not escaped already. - * - * Rules: - * <ul> - * <li>Double quotes are needed if string starts or ends with at least one space. - * <li>{@code @, ?} at beginning of string have to be escaped with a backslash. - * <li>{@code ', ", \} have to be escaped with a backslash. - * <li>{@code <, >, &} have to be replaced by their predefined xml entity. - * <li>{@code \n, \t} have to be replaced by a backslash and the appropriate character. - * </ul> - * @param s the string to be escaped - * @return the escaped string as it would appear in the XML text in a values file - */ - public static String escapeResourceString(String s) { - int n = s.length(); - if (n == 0) { - return ""; - } - - StringBuilder sb = new StringBuilder(s.length() * 2); - boolean hasSpace = s.charAt(0) == ' ' || s.charAt(n - 1) == ' '; - - if (hasSpace) { - sb.append('"'); - } else if (s.charAt(0) == '@' || s.charAt(0) == '?') { - sb.append('\\'); - } - - for (int i = 0; i < n; ++i) { - char c = s.charAt(i); - switch (c) { - case '\'': - if (!hasSpace) { - sb.append('\\'); - } - sb.append(c); - break; - case '"': - case '\\': - sb.append('\\'); - sb.append(c); - break; - case '<': - sb.append(LT_ENTITY); - break; - case '&': - sb.append(AMP_ENTITY); - break; - case '\n': - sb.append("\\n"); //$NON-NLS-1$ - break; - case '\t': - sb.append("\\t"); //$NON-NLS-1$ - break; - default: - sb.append(c); - break; - } - } - - if (hasSpace) { - sb.append('"'); - } - - return sb.toString(); - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/Configurable.java b/sdk_common/src/com/android/ide/common/resources/configuration/Configurable.java deleted file mode 100644 index 5e7f910..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/Configurable.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - - -/** - * An object that is associated with a {@link FolderConfiguration}. - */ -public interface Configurable { - /** - * Returns the {@link FolderConfiguration} for this object. - */ - public FolderConfiguration getConfiguration(); -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/CountryCodeQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/CountryCodeQualifier.java deleted file mode 100644 index eb7cc0d..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/CountryCodeQualifier.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.resources.configuration; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Resource Qualifier for Mobile Country Code. - */ -public final class CountryCodeQualifier extends ResourceQualifier { - /** Default pixel density value. This means the property is not set. */ - private final static int DEFAULT_CODE = -1; - - private final static Pattern sCountryCodePattern = Pattern.compile("^mcc(\\d{3})$");//$NON-NLS-1$ - - private final int mCode; - - public static final String NAME = "Mobile Country Code"; - - /** - * Creates and returns a qualifier from the given folder segment. If the segment is incorrect, - * <code>null</code> is returned. - * @param segment the folder segment from which to create a qualifier. - * @return a new {@link CountryCodeQualifier} object or <code>null</code> - */ - public static CountryCodeQualifier getQualifier(String segment) { - Matcher m = sCountryCodePattern.matcher(segment); - if (m.matches()) { - String v = m.group(1); - - int code = -1; - try { - code = Integer.parseInt(v); - } catch (NumberFormatException e) { - // looks like the string we extracted wasn't a valid number. - return null; - } - - CountryCodeQualifier qualifier = new CountryCodeQualifier(code); - return qualifier; - } - - return null; - } - - /** - * Returns the folder name segment for the given value. This is equivalent to calling - * {@link #toString()} on a {@link CountryCodeQualifier} object. - * @param code the value of the qualifier, as returned by {@link #getCode()}. - */ - public static String getFolderSegment(int code) { - if (code != DEFAULT_CODE && code >= 100 && code <=999) { // code is 3 digit.) { - return String.format("mcc%1$d", code); //$NON-NLS-1$ - } - - return ""; //$NON-NLS-1$ - } - - public CountryCodeQualifier() { - this(DEFAULT_CODE); - } - - public CountryCodeQualifier(int code) { - mCode = code; - } - - public int getCode() { - return mCode; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return "Country Code"; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean isValid() { - return mCode != DEFAULT_CODE; - } - - @Override - public boolean hasFakeValue() { - return false; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - CountryCodeQualifier qualifier = getQualifier(value); - if (qualifier != null) { - config.setCountryCodeQualifier(qualifier); - return true; - } - - return false; - } - - @Override - public boolean equals(Object qualifier) { - if (qualifier instanceof CountryCodeQualifier) { - return mCode == ((CountryCodeQualifier)qualifier).mCode; - } - - return false; - } - - @Override - public int hashCode() { - return mCode; - } - - /** - * Returns the string used to represent this qualifier in the folder name. - */ - @Override - public String getFolderSegment() { - return getFolderSegment(mCode); - } - - @Override - public String getShortDisplayValue() { - if (mCode != DEFAULT_CODE) { - return String.format("MCC %1$d", mCode); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getLongDisplayValue() { - return getShortDisplayValue(); - } - -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/DensityQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/DensityQualifier.java deleted file mode 100644 index a9e4a01..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/DensityQualifier.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.resources.Density; -import com.android.resources.ResourceEnum; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Resource Qualifier for Screen Pixel Density. - */ -public final class DensityQualifier extends EnumBasedResourceQualifier { - private final static Pattern sDensityLegacyPattern = Pattern.compile("^(\\d+)dpi$");//$NON-NLS-1$ - - public static final String NAME = "Density"; - - private Density mValue = Density.MEDIUM; - - public DensityQualifier() { - // pass - } - - public DensityQualifier(Density value) { - mValue = value; - } - - public Density getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 4; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - Density density = Density.getEnum(value); - if (density == null) { - - // attempt to read a legacy value. - Matcher m = sDensityLegacyPattern.matcher(value); - if (m.matches()) { - String v = m.group(1); - - try { - density = Density.getEnum(Integer.parseInt(v)); - } catch (NumberFormatException e) { - // looks like the string we extracted wasn't a valid number - // which really shouldn't happen since the regexp would have failed. - } - } - } - - if (density != null) { - DensityQualifier qualifier = new DensityQualifier(); - qualifier.mValue = density; - config.setDensityQualifier(qualifier); - return true; - } - - return false; - } - - @Override - public boolean isMatchFor(ResourceQualifier qualifier) { - if (qualifier instanceof DensityQualifier) { - // as long as there's a density qualifier, it's always a match. - // The best match will be found later. - return true; - } - - return false; - } - - @Override - public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) { - if (compareTo == null) { - return true; - } - - DensityQualifier compareQ = (DensityQualifier)compareTo; - DensityQualifier referenceQ = (DensityQualifier)reference; - - if (compareQ.mValue == referenceQ.mValue) { - // what we have is already the best possible match (exact match) - return false; - } else if (mValue == referenceQ.mValue) { - // got new exact value, this is the best! - return true; - } else { - // in all case we're going to prefer the higher dpi. - // if reference is high, we want highest dpi. - // if reference is medium, we'll prefer to scale down high dpi, than scale up low dpi - // if reference if low, we'll prefer to scale down high than medium (2:1 over 4:3) - return mValue.getDpiValue() > compareQ.mValue.getDpiValue(); - } - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/DeviceConfigHelper.java b/sdk_common/src/com/android/ide/common/resources/configuration/DeviceConfigHelper.java deleted file mode 100644 index 27eaa01..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/DeviceConfigHelper.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.ide.common.resources.configuration; - -import com.android.annotations.Nullable; -import com.android.resources.NightMode; -import com.android.resources.UiMode; -import com.android.sdklib.devices.Device; -import com.android.sdklib.devices.Hardware; -import com.android.sdklib.devices.Screen; -import com.android.sdklib.devices.State; - -public class DeviceConfigHelper { - /** - * Returns a {@link FolderConfiguration} based on the given state - * - * @param state - * The {@link State} of the {@link Device} to base the - * {@link FolderConfiguration} on. Can be null. - * @return A {@link FolderConfiguration} based on the given {@link State}. - * If the given {@link State} is null, the result is also null; - */ - @Nullable - public static FolderConfiguration getFolderConfig(@Nullable State state) { - if (state == null) { - return null; - } - - Hardware hw = state.getHardware(); - - FolderConfiguration config = new FolderConfiguration(); - config.createDefault(); - Screen screen = hw.getScreen(); - config.setDensityQualifier(new DensityQualifier(screen.getPixelDensity())); - config.setNavigationMethodQualifier(new NavigationMethodQualifier(hw.getNav())); - ScreenDimensionQualifier sdq; - if (screen.getXDimension() > screen.getYDimension()) { - sdq = new ScreenDimensionQualifier(screen.getXDimension(), screen.getYDimension()); - } else { - sdq = new ScreenDimensionQualifier(screen.getYDimension(), screen.getXDimension()); - } - config.setScreenDimensionQualifier(sdq); - config.setScreenRatioQualifier(new ScreenRatioQualifier(screen.getRatio())); - config.setScreenSizeQualifier(new ScreenSizeQualifier(screen.getSize())); - config.setTextInputMethodQualifier(new TextInputMethodQualifier(hw.getKeyboard())); - config.setTouchTypeQualifier(new TouchScreenQualifier(screen.getMechanism())); - - config.setKeyboardStateQualifier(new KeyboardStateQualifier(state.getKeyState())); - config.setNavigationStateQualifier(new NavigationStateQualifier(state.getNavState())); - config.setScreenOrientationQualifier( - new ScreenOrientationQualifier(state.getOrientation())); - - config.updateScreenWidthAndHeight(); - - // Setup some default qualifiers - config.setUiModeQualifier(new UiModeQualifier(UiMode.NORMAL)); - config.setNightModeQualifier(new NightModeQualifier(NightMode.NOTNIGHT)); - config.setCountryCodeQualifier(new CountryCodeQualifier()); - config.setLanguageQualifier(new LanguageQualifier()); - config.setNetworkCodeQualifier(new NetworkCodeQualifier()); - config.setRegionQualifier(new RegionQualifier()); - config.setVersionQualifier(new VersionQualifier()); - - return config; - } - - /** - * Returns a {@link FolderConfiguration} based on the {@link State} given by - * the {@link Device} and the state name. - * - * @param d - * The {@link Device} to base the {@link FolderConfiguration} on. - * @param stateName - * The name of the state to base the {@link FolderConfiguration} - * on. - * @return The {@link FolderConfiguration} based on the determined - * {@link State}. If there is no {@link State} with the given state - * name for the given device, null is returned. - */ - @Nullable - public static FolderConfiguration getFolderConfig(Device d, String stateName) { - return getFolderConfig(d.getState(stateName)); - } - - /** - * Returns a {@link FolderConfiguration} based on the default {@link State} - * for the given {@link Device}. - * - * @param d - * The {@link Device} to generate the {@link FolderConfiguration} - * from. - * @return A {@link FolderConfiguration} based on the default {@link State} - * for the given {@link Device} - */ - public static FolderConfiguration getFolderConfig(Device d) { - return getFolderConfig(d.getDefaultState()); - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/EnumBasedResourceQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/EnumBasedResourceQualifier.java deleted file mode 100644 index 7bfda2d..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/EnumBasedResourceQualifier.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.resources.configuration; - -import com.android.resources.ResourceEnum; - -/** - * Base class for {@link ResourceQualifier} whose value is an {@link ResourceEnum}. - * - */ -abstract class EnumBasedResourceQualifier extends ResourceQualifier { - - abstract ResourceEnum getEnumValue(); - - @Override - public boolean isValid() { - return getEnumValue() != null; - } - - @Override - public boolean hasFakeValue() { - return getEnumValue().isFakeValue(); - } - - @Override - public boolean equals(Object qualifier) { - if (qualifier instanceof EnumBasedResourceQualifier) { - return getEnumValue() == ((EnumBasedResourceQualifier)qualifier).getEnumValue(); - } - - return false; - } - - @Override - public int hashCode() { - ResourceEnum value = getEnumValue(); - if (value != null) { - return value.hashCode(); - } - - return 0; - } - - /** - * Returns the string used to represent this qualifier in the folder name. - */ - @Override - public final String getFolderSegment() { - ResourceEnum value = getEnumValue(); - if (value != null) { - return value.getResourceValue(); - } - - return ""; //$NON-NLS-1$ - } - - - @Override - public String getShortDisplayValue() { - ResourceEnum value = getEnumValue(); - if (value != null) { - return value.getShortDisplayValue(); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getLongDisplayValue() { - ResourceEnum value = getEnumValue(); - if (value != null) { - return value.getLongDisplayValue(); - } - - return ""; //$NON-NLS-1$ - } - -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/FolderConfiguration.java b/sdk_common/src/com/android/ide/common/resources/configuration/FolderConfiguration.java deleted file mode 100644 index fc83359..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/FolderConfiguration.java +++ /dev/null @@ -1,946 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.SdkConstants; -import com.android.resources.Density; -import com.android.resources.ResourceFolderType; -import com.android.resources.ScreenOrientation; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - - -/** - * Represents the configuration for Resource Folders. All the properties have a default - * value which means that the property is not set. - */ -public final class FolderConfiguration implements Comparable<FolderConfiguration> { - - private final static ResourceQualifier[] DEFAULT_QUALIFIERS; - - static { - // get the default qualifiers. - FolderConfiguration defaultConfig = new FolderConfiguration(); - defaultConfig.createDefault(); - DEFAULT_QUALIFIERS = defaultConfig.getQualifiers(); - } - - - private final ResourceQualifier[] mQualifiers = new ResourceQualifier[INDEX_COUNT]; - - private final static int INDEX_COUNTRY_CODE = 0; - private final static int INDEX_NETWORK_CODE = 1; - private final static int INDEX_LANGUAGE = 2; - private final static int INDEX_REGION = 3; - private final static int INDEX_LAYOUTDIR = 4; - private final static int INDEX_SMALLEST_SCREEN_WIDTH = 5; - private final static int INDEX_SCREEN_WIDTH = 6; - private final static int INDEX_SCREEN_HEIGHT = 7; - private final static int INDEX_SCREEN_LAYOUT_SIZE = 8; - private final static int INDEX_SCREEN_RATIO = 9; - private final static int INDEX_SCREEN_ORIENTATION = 10; - private final static int INDEX_UI_MODE = 11; - private final static int INDEX_NIGHT_MODE = 12; - private final static int INDEX_PIXEL_DENSITY = 13; - private final static int INDEX_TOUCH_TYPE = 14; - private final static int INDEX_KEYBOARD_STATE = 15; - private final static int INDEX_TEXT_INPUT_METHOD = 16; - private final static int INDEX_NAVIGATION_STATE = 17; - private final static int INDEX_NAVIGATION_METHOD = 18; - private final static int INDEX_SCREEN_DIMENSION = 19; - private final static int INDEX_VERSION = 20; - private final static int INDEX_COUNT = 21; - - /** - * Creates a {@link FolderConfiguration} matching the folder segments. - * @param folderSegments The segments of the folder name. The first segments should contain - * the name of the folder - * @return a FolderConfiguration object, or null if the folder name isn't valid.. - */ - public static FolderConfiguration getConfig(String[] folderSegments) { - FolderConfiguration config = new FolderConfiguration(); - - // we are going to loop through the segments, and match them with the first - // available qualifier. If the segment doesn't match we try with the next qualifier. - // Because the order of the qualifier is fixed, we do not reset the first qualifier - // after each successful segment. - // If we run out of qualifier before processing all the segments, we fail. - - int qualifierIndex = 0; - int qualifierCount = DEFAULT_QUALIFIERS.length; - - for (int i = 1 ; i < folderSegments.length; i++) { - String seg = folderSegments[i]; - if (seg.length() > 0) { - while (qualifierIndex < qualifierCount && - DEFAULT_QUALIFIERS[qualifierIndex].checkAndSet(seg, config) == false) { - qualifierIndex++; - } - - // if we reached the end of the qualifier we didn't find a matching qualifier. - if (qualifierIndex == qualifierCount) { - return null; - } - - } else { - return null; - } - } - - return config; - } - - /** - * Creates a {@link FolderConfiguration} matching the folder segments. - * @param folderSegments The segments of the folder name. The first segments should contain - * the name of the folder - * @return a FolderConfiguration object, or null if the folder name isn't valid.. - * @see FolderConfiguration#getConfig(String[]) - */ - public static FolderConfiguration getConfig(Iterable<String>folderSegments) { - FolderConfiguration config = new FolderConfiguration(); - - // we are going to loop through the segments, and match them with the first - // available qualifier. If the segment doesn't match we try with the next qualifier. - // Because the order of the qualifier is fixed, we do not reset the first qualifier - // after each successful segment. - // If we run out of qualifier before processing all the segments, we fail. - - int qualifierIndex = 0; - int qualifierCount = DEFAULT_QUALIFIERS.length; - - Iterator<String> iterator = folderSegments.iterator(); - if (iterator.hasNext()) { - // Skip the first segment: it should be just the base folder, such as "values" or - // "layout" - iterator.next(); - } - while (iterator.hasNext()) { - String seg = iterator.next(); - if (seg.length() > 0) { - while (qualifierIndex < qualifierCount && - DEFAULT_QUALIFIERS[qualifierIndex].checkAndSet(seg, config) == false) { - qualifierIndex++; - } - - // if we reached the end of the qualifier we didn't find a matching qualifier. - if (qualifierIndex == qualifierCount) { - return null; - } - - } else { - return null; - } - } - - return config; - } - - /** - * Returns the number of {@link ResourceQualifier} that make up a Folder configuration. - */ - public static int getQualifierCount() { - return INDEX_COUNT; - } - - /** - * Sets the config from the qualifiers of a given <var>config</var>. - * <p/>This is equivalent to <code>set(config, false)</code> - * @param config the configuration to set - * - * @see #set(FolderConfiguration, boolean) - */ - public void set(FolderConfiguration config) { - set(config, false /*nonFakeValuesOnly*/); - } - - /** - * Sets the config from the qualifiers of a given <var>config</var>. - * @param config the configuration to set - * @param nonFakeValuesOnly if set to true this ignore qualifiers for which the - * current value is a fake value. - * - * @see ResourceQualifier#hasFakeValue() - */ - public void set(FolderConfiguration config, boolean nonFakeValuesOnly) { - if (config != null) { - for (int i = 0 ; i < INDEX_COUNT ; i++) { - ResourceQualifier q = config.mQualifiers[i]; - if (nonFakeValuesOnly == false || q == null || q.hasFakeValue() == false) { - mQualifiers[i] = q; - } - } - } - } - - /** - * Reset the config. - * <p/>This makes qualifiers at all indices <code>null</code>. - */ - public void reset() { - for (int i = 0 ; i < INDEX_COUNT ; i++) { - mQualifiers[i] = null; - } - } - - /** - * Removes the qualifiers from the receiver if they are present (and valid) - * in the given configuration. - */ - public void substract(FolderConfiguration config) { - for (int i = 0 ; i < INDEX_COUNT ; i++) { - if (config.mQualifiers[i] != null && config.mQualifiers[i].isValid()) { - mQualifiers[i] = null; - } - } - } - - /** - * Adds the non-qualifiers from the given config. - * Qualifiers that are null in the given config do not change in the receiver. - */ - public void add(FolderConfiguration config) { - for (int i = 0 ; i < INDEX_COUNT ; i++) { - if (config.mQualifiers[i] != null) { - mQualifiers[i] = config.mQualifiers[i]; - } - } - } - - /** - * Returns the first invalid qualifier, or <code>null<code> if they are all valid (or if none - * exists). - */ - public ResourceQualifier getInvalidQualifier() { - for (int i = 0 ; i < INDEX_COUNT ; i++) { - if (mQualifiers[i] != null && mQualifiers[i].isValid() == false) { - return mQualifiers[i]; - } - } - - // all allocated qualifiers are valid, we return null. - return null; - } - - /** - * Returns whether the Region qualifier is valid. Region qualifier can only be present if a - * Language qualifier is present as well. - * @return true if the Region qualifier is valid. - */ - public boolean checkRegion() { - if (mQualifiers[INDEX_LANGUAGE] == null && mQualifiers[INDEX_REGION] != null) { - return false; - } - - return true; - } - - /** - * Adds a qualifier to the {@link FolderConfiguration} - * @param qualifier the {@link ResourceQualifier} to add. - */ - public void addQualifier(ResourceQualifier qualifier) { - if (qualifier instanceof CountryCodeQualifier) { - mQualifiers[INDEX_COUNTRY_CODE] = qualifier; - - } else if (qualifier instanceof NetworkCodeQualifier) { - mQualifiers[INDEX_NETWORK_CODE] = qualifier; - - } else if (qualifier instanceof LanguageQualifier) { - mQualifiers[INDEX_LANGUAGE] = qualifier; - - } else if (qualifier instanceof RegionQualifier) { - mQualifiers[INDEX_REGION] = qualifier; - - } else if (qualifier instanceof LayoutDirectionQualifier) { - mQualifiers[INDEX_LAYOUTDIR] = qualifier; - - } else if (qualifier instanceof SmallestScreenWidthQualifier) { - mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = qualifier; - - } else if (qualifier instanceof ScreenWidthQualifier) { - mQualifiers[INDEX_SCREEN_WIDTH] = qualifier; - - } else if (qualifier instanceof ScreenHeightQualifier) { - mQualifiers[INDEX_SCREEN_HEIGHT] = qualifier; - - } else if (qualifier instanceof ScreenSizeQualifier) { - mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = qualifier; - - } else if (qualifier instanceof ScreenRatioQualifier) { - mQualifiers[INDEX_SCREEN_RATIO] = qualifier; - - } else if (qualifier instanceof ScreenOrientationQualifier) { - mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier; - - } else if (qualifier instanceof UiModeQualifier) { - mQualifiers[INDEX_UI_MODE] = qualifier; - - } else if (qualifier instanceof NightModeQualifier) { - mQualifiers[INDEX_NIGHT_MODE] = qualifier; - - } else if (qualifier instanceof DensityQualifier) { - mQualifiers[INDEX_PIXEL_DENSITY] = qualifier; - - } else if (qualifier instanceof TouchScreenQualifier) { - mQualifiers[INDEX_TOUCH_TYPE] = qualifier; - - } else if (qualifier instanceof KeyboardStateQualifier) { - mQualifiers[INDEX_KEYBOARD_STATE] = qualifier; - - } else if (qualifier instanceof TextInputMethodQualifier) { - mQualifiers[INDEX_TEXT_INPUT_METHOD] = qualifier; - - } else if (qualifier instanceof NavigationStateQualifier) { - mQualifiers[INDEX_NAVIGATION_STATE] = qualifier; - - } else if (qualifier instanceof NavigationMethodQualifier) { - mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier; - - } else if (qualifier instanceof ScreenDimensionQualifier) { - mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier; - - } else if (qualifier instanceof VersionQualifier) { - mQualifiers[INDEX_VERSION] = qualifier; - - } - } - - /** - * Removes a given qualifier from the {@link FolderConfiguration}. - * @param qualifier the {@link ResourceQualifier} to remove. - */ - public void removeQualifier(ResourceQualifier qualifier) { - for (int i = 0 ; i < INDEX_COUNT ; i++) { - if (mQualifiers[i] == qualifier) { - mQualifiers[i] = null; - return; - } - } - } - - /** - * Returns a qualifier by its index. The total number of qualifiers can be accessed by - * {@link #getQualifierCount()}. - * @param index the index of the qualifier to return. - * @return the qualifier or null if there are none at the index. - */ - public ResourceQualifier getQualifier(int index) { - return mQualifiers[index]; - } - - public void setCountryCodeQualifier(CountryCodeQualifier qualifier) { - mQualifiers[INDEX_COUNTRY_CODE] = qualifier; - } - - public CountryCodeQualifier getCountryCodeQualifier() { - return (CountryCodeQualifier)mQualifiers[INDEX_COUNTRY_CODE]; - } - - public void setNetworkCodeQualifier(NetworkCodeQualifier qualifier) { - mQualifiers[INDEX_NETWORK_CODE] = qualifier; - } - - public NetworkCodeQualifier getNetworkCodeQualifier() { - return (NetworkCodeQualifier)mQualifiers[INDEX_NETWORK_CODE]; - } - - public void setLanguageQualifier(LanguageQualifier qualifier) { - mQualifiers[INDEX_LANGUAGE] = qualifier; - } - - public LanguageQualifier getLanguageQualifier() { - return (LanguageQualifier)mQualifiers[INDEX_LANGUAGE]; - } - - public void setRegionQualifier(RegionQualifier qualifier) { - mQualifiers[INDEX_REGION] = qualifier; - } - - public RegionQualifier getRegionQualifier() { - return (RegionQualifier)mQualifiers[INDEX_REGION]; - } - - public void setLayoutDirectionQualifier(LayoutDirectionQualifier qualifier) { - mQualifiers[INDEX_LAYOUTDIR] = qualifier; - } - - public LayoutDirectionQualifier getLayoutDirectionQualifier() { - return (LayoutDirectionQualifier)mQualifiers[INDEX_LAYOUTDIR]; - } - - public void setSmallestScreenWidthQualifier(SmallestScreenWidthQualifier qualifier) { - mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = qualifier; - } - - public SmallestScreenWidthQualifier getSmallestScreenWidthQualifier() { - return (SmallestScreenWidthQualifier) mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH]; - } - - public void setScreenWidthQualifier(ScreenWidthQualifier qualifier) { - mQualifiers[INDEX_SCREEN_WIDTH] = qualifier; - } - - public ScreenWidthQualifier getScreenWidthQualifier() { - return (ScreenWidthQualifier) mQualifiers[INDEX_SCREEN_WIDTH]; - } - - public void setScreenHeightQualifier(ScreenHeightQualifier qualifier) { - mQualifiers[INDEX_SCREEN_HEIGHT] = qualifier; - } - - public ScreenHeightQualifier getScreenHeightQualifier() { - return (ScreenHeightQualifier) mQualifiers[INDEX_SCREEN_HEIGHT]; - } - - public void setScreenSizeQualifier(ScreenSizeQualifier qualifier) { - mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = qualifier; - } - - public ScreenSizeQualifier getScreenSizeQualifier() { - return (ScreenSizeQualifier)mQualifiers[INDEX_SCREEN_LAYOUT_SIZE]; - } - - public void setScreenRatioQualifier(ScreenRatioQualifier qualifier) { - mQualifiers[INDEX_SCREEN_RATIO] = qualifier; - } - - public ScreenRatioQualifier getScreenRatioQualifier() { - return (ScreenRatioQualifier)mQualifiers[INDEX_SCREEN_RATIO]; - } - - public void setScreenOrientationQualifier(ScreenOrientationQualifier qualifier) { - mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier; - } - - public ScreenOrientationQualifier getScreenOrientationQualifier() { - return (ScreenOrientationQualifier)mQualifiers[INDEX_SCREEN_ORIENTATION]; - } - - public void setUiModeQualifier(UiModeQualifier qualifier) { - mQualifiers[INDEX_UI_MODE] = qualifier; - } - - public UiModeQualifier getUiModeQualifier() { - return (UiModeQualifier)mQualifiers[INDEX_UI_MODE]; - } - - public void setNightModeQualifier(NightModeQualifier qualifier) { - mQualifiers[INDEX_NIGHT_MODE] = qualifier; - } - - public NightModeQualifier getNightModeQualifier() { - return (NightModeQualifier)mQualifiers[INDEX_NIGHT_MODE]; - } - - public void setDensityQualifier(DensityQualifier qualifier) { - mQualifiers[INDEX_PIXEL_DENSITY] = qualifier; - } - - public DensityQualifier getDensityQualifier() { - return (DensityQualifier)mQualifiers[INDEX_PIXEL_DENSITY]; - } - - public void setTouchTypeQualifier(TouchScreenQualifier qualifier) { - mQualifiers[INDEX_TOUCH_TYPE] = qualifier; - } - - public TouchScreenQualifier getTouchTypeQualifier() { - return (TouchScreenQualifier)mQualifiers[INDEX_TOUCH_TYPE]; - } - - public void setKeyboardStateQualifier(KeyboardStateQualifier qualifier) { - mQualifiers[INDEX_KEYBOARD_STATE] = qualifier; - } - - public KeyboardStateQualifier getKeyboardStateQualifier() { - return (KeyboardStateQualifier)mQualifiers[INDEX_KEYBOARD_STATE]; - } - - public void setTextInputMethodQualifier(TextInputMethodQualifier qualifier) { - mQualifiers[INDEX_TEXT_INPUT_METHOD] = qualifier; - } - - public TextInputMethodQualifier getTextInputMethodQualifier() { - return (TextInputMethodQualifier)mQualifiers[INDEX_TEXT_INPUT_METHOD]; - } - - public void setNavigationStateQualifier(NavigationStateQualifier qualifier) { - mQualifiers[INDEX_NAVIGATION_STATE] = qualifier; - } - - public NavigationStateQualifier getNavigationStateQualifier() { - return (NavigationStateQualifier)mQualifiers[INDEX_NAVIGATION_STATE]; - } - - public void setNavigationMethodQualifier(NavigationMethodQualifier qualifier) { - mQualifiers[INDEX_NAVIGATION_METHOD] = qualifier; - } - - public NavigationMethodQualifier getNavigationMethodQualifier() { - return (NavigationMethodQualifier)mQualifiers[INDEX_NAVIGATION_METHOD]; - } - - public void setScreenDimensionQualifier(ScreenDimensionQualifier qualifier) { - mQualifiers[INDEX_SCREEN_DIMENSION] = qualifier; - } - - public ScreenDimensionQualifier getScreenDimensionQualifier() { - return (ScreenDimensionQualifier)mQualifiers[INDEX_SCREEN_DIMENSION]; - } - - public void setVersionQualifier(VersionQualifier qualifier) { - mQualifiers[INDEX_VERSION] = qualifier; - } - - public VersionQualifier getVersionQualifier() { - return (VersionQualifier)mQualifiers[INDEX_VERSION]; - } - - /** - * Updates the {@link SmallestScreenWidthQualifier}, {@link ScreenWidthQualifier}, and - * {@link ScreenHeightQualifier} based on the (required) values of - * {@link ScreenDimensionQualifier} {@link DensityQualifier}, and - * {@link ScreenOrientationQualifier}. - * - * Also the density cannot be {@link Density#NODPI} as it's not valid on a device. - */ - public void updateScreenWidthAndHeight() { - - ResourceQualifier sizeQ = mQualifiers[INDEX_SCREEN_DIMENSION]; - ResourceQualifier densityQ = mQualifiers[INDEX_PIXEL_DENSITY]; - ResourceQualifier orientQ = mQualifiers[INDEX_SCREEN_ORIENTATION]; - - if (sizeQ != null && densityQ != null && orientQ != null) { - Density density = ((DensityQualifier) densityQ).getValue(); - if (density == Density.NODPI) { - return; - } - - ScreenOrientation orientation = ((ScreenOrientationQualifier) orientQ).getValue(); - - int size1 = ((ScreenDimensionQualifier) sizeQ).getValue1(); - int size2 = ((ScreenDimensionQualifier) sizeQ).getValue2(); - - // make sure size1 is the biggest (should be the case, but make sure) - if (size1 < size2) { - int a = size1; - size1 = size2; - size2 = a; - } - - // compute the dp. round them up since we want -w480dp to match a 480.5dp screen - int dp1 = (int) Math.ceil(size1 * Density.DEFAULT_DENSITY / density.getDpiValue()); - int dp2 = (int) Math.ceil(size2 * Density.DEFAULT_DENSITY / density.getDpiValue()); - - setSmallestScreenWidthQualifier(new SmallestScreenWidthQualifier(dp2)); - - switch (orientation) { - case PORTRAIT: - setScreenWidthQualifier(new ScreenWidthQualifier(dp2)); - setScreenHeightQualifier(new ScreenHeightQualifier(dp1)); - break; - case LANDSCAPE: - setScreenWidthQualifier(new ScreenWidthQualifier(dp1)); - setScreenHeightQualifier(new ScreenHeightQualifier(dp2)); - break; - case SQUARE: - setScreenWidthQualifier(new ScreenWidthQualifier(dp2)); - setScreenHeightQualifier(new ScreenHeightQualifier(dp2)); - break; - } - } - } - - /** - * Returns whether an object is equals to the receiver. - */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (obj instanceof FolderConfiguration) { - FolderConfiguration fc = (FolderConfiguration)obj; - for (int i = 0 ; i < INDEX_COUNT ; i++) { - ResourceQualifier qualifier = mQualifiers[i]; - ResourceQualifier fcQualifier = fc.mQualifiers[i]; - if (qualifier != null) { - if (qualifier.equals(fcQualifier) == false) { - return false; - } - } else if (fcQualifier != null) { - return false; - } - } - - return true; - } - - return false; - } - - @Override - public int hashCode() { - return toString().hashCode(); - } - - /** - * Returns whether the Configuration has only default values. - */ - public boolean isDefault() { - for (ResourceQualifier irq : mQualifiers) { - if (irq != null) { - return false; - } - } - - return true; - } - - /** - * Returns the name of a folder with the configuration. - */ - public String getFolderName(ResourceFolderType folder) { - StringBuilder result = new StringBuilder(folder.getName()); - - for (ResourceQualifier qualifier : mQualifiers) { - if (qualifier != null) { - String segment = qualifier.getFolderSegment(); - if (segment != null && segment.length() > 0) { - result.append(SdkConstants.RES_QUALIFIER_SEP); - result.append(segment); - } - } - } - - return result.toString(); - } - - /** - * Returns {@link #toDisplayString()}. - */ - @Override - public String toString() { - return toDisplayString(); - } - - /** - * Returns a string valid for display purpose. - */ - public String toDisplayString() { - if (isDefault()) { - return "default"; - } - - StringBuilder result = null; - int index = 0; - ResourceQualifier qualifier = null; - - // pre- language/region qualifiers - while (index < INDEX_LANGUAGE) { - qualifier = mQualifiers[index++]; - if (qualifier != null) { - if (result == null) { - result = new StringBuilder(); - } else { - result.append(", "); //$NON-NLS-1$ - } - result.append(qualifier.getLongDisplayValue()); - - } - } - - // process the language/region qualifier in a custom way, if there are both non null. - if (mQualifiers[INDEX_LANGUAGE] != null && mQualifiers[INDEX_REGION] != null) { - String language = mQualifiers[INDEX_LANGUAGE].getLongDisplayValue(); - String region = mQualifiers[INDEX_REGION].getLongDisplayValue(); - - if (result == null) { - result = new StringBuilder(); - } else { - result.append(", "); //$NON-NLS-1$ - } - result.append(String.format("Locale %s_%s", language, region)); //$NON-NLS-1$ - - index += 2; - } - - // post language/region qualifiers. - while (index < INDEX_COUNT) { - qualifier = mQualifiers[index++]; - if (qualifier != null) { - if (result == null) { - result = new StringBuilder(); - } else { - result.append(", "); //$NON-NLS-1$ - } - result.append(qualifier.getLongDisplayValue()); - - } - } - - return result == null ? null : result.toString(); - } - - @Override - public int compareTo(FolderConfiguration folderConfig) { - // default are always at the top. - if (isDefault()) { - if (folderConfig.isDefault()) { - return 0; - } - return -1; - } - - // now we compare the qualifiers - for (int i = 0 ; i < INDEX_COUNT; i++) { - ResourceQualifier qualifier1 = mQualifiers[i]; - ResourceQualifier qualifier2 = folderConfig.mQualifiers[i]; - - if (qualifier1 == null) { - if (qualifier2 == null) { - continue; - } else { - return -1; - } - } else { - if (qualifier2 == null) { - return 1; - } else { - int result = qualifier1.compareTo(qualifier2); - - if (result == 0) { - continue; - } - - return result; - } - } - } - - // if we arrive here, all the qualifier matches - return 0; - } - - /** - * Returns the best matching {@link Configurable} for this configuration. - * - * @param configurables the list of {@link Configurable} to choose from. - * - * @return an item from the given list of {@link Configurable} or null. - * - * @see http://d.android.com/guide/topics/resources/resources-i18n.html#best-match - */ - public Configurable findMatchingConfigurable(List<? extends Configurable> configurables) { - // - // 1: eliminate resources that contradict the reference configuration - // 2: pick next qualifier type - // 3: check if any resources use this qualifier, if no, back to 2, else move on to 4. - // 4: eliminate resources that don't use this qualifier. - // 5: if more than one resource left, go back to 2. - // - // The precedence of the qualifiers is more important than the number of qualifiers that - // exactly match the device. - - // 1: eliminate resources that contradict - ArrayList<Configurable> matchingConfigurables = new ArrayList<Configurable>(); - for (int i = 0 ; i < configurables.size(); i++) { - Configurable res = configurables.get(i); - - if (res.getConfiguration().isMatchFor(this)) { - matchingConfigurables.add(res); - } - } - - // if there is only one match, just take it - if (matchingConfigurables.size() == 1) { - return matchingConfigurables.get(0); - } else if (matchingConfigurables.size() == 0) { - return null; - } - - // 2. Loop on the qualifiers, and eliminate matches - final int count = FolderConfiguration.getQualifierCount(); - for (int q = 0 ; q < count ; q++) { - // look to see if one configurable has this qualifier. - // At the same time also record the best match value for the qualifier (if applicable). - - // The reference value, to find the best match. - // Note that this qualifier could be null. In which case any qualifier found in the - // possible match, will all be considered best match. - ResourceQualifier referenceQualifier = getQualifier(q); - - boolean found = false; - ResourceQualifier bestMatch = null; // this is to store the best match. - for (Configurable configurable : matchingConfigurables) { - ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q); - if (qualifier != null) { - // set the flag. - found = true; - - // Now check for a best match. If the reference qualifier is null , - // any qualifier is a "best" match (we don't need to record all of them. - // Instead the non compatible ones are removed below) - if (referenceQualifier != null) { - if (qualifier.isBetterMatchThan(bestMatch, referenceQualifier)) { - bestMatch = qualifier; - } - } - } - } - - // 4. If a configurable has a qualifier at the current index, remove all the ones that - // do not have one, or whose qualifier value does not equal the best match found above - // unless there's no reference qualifier, in which case they are all considered - // "best" match. - if (found) { - for (int i = 0 ; i < matchingConfigurables.size(); ) { - Configurable configurable = matchingConfigurables.get(i); - ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q); - - if (qualifier == null) { - // this resources has no qualifier of this type: rejected. - matchingConfigurables.remove(configurable); - } else if (referenceQualifier != null && bestMatch != null && - bestMatch.equals(qualifier) == false) { - // there's a reference qualifier and there is a better match for it than - // this resource, so we reject it. - matchingConfigurables.remove(configurable); - } else { - // looks like we keep this resource, move on to the next one. - i++; - } - } - - // at this point we may have run out of matching resources before going - // through all the qualifiers. - if (matchingConfigurables.size() < 2) { - break; - } - } - } - - // Because we accept resources whose configuration have qualifiers where the reference - // configuration doesn't, we can end up with more than one match. In this case, we just - // take the first one. - if (matchingConfigurables.size() == 0) { - return null; - } - return matchingConfigurables.get(0); - } - - - /** - * Returns whether the configuration is a match for the given reference config. - * <p/>A match means that, for each qualifier of this config - * <ul> - * <li>The reference config has no value set - * <li>or, the qualifier of the reference config is a match. Depending on the qualifier type - * this does not mean the same exact value.</li> - * </ul> - * @param referenceConfig The reference configuration to test against. - * @return true if the configuration matches. - */ - public boolean isMatchFor(FolderConfiguration referenceConfig) { - if (referenceConfig == null) { - return false; - } - - for (int i = 0 ; i < INDEX_COUNT ; i++) { - ResourceQualifier testQualifier = mQualifiers[i]; - ResourceQualifier referenceQualifier = referenceConfig.mQualifiers[i]; - - // it's only a non match if both qualifiers are non-null, and they don't match. - if (testQualifier != null && referenceQualifier != null && - testQualifier.isMatchFor(referenceQualifier) == false) { - return false; - } - } - - return true; - } - - /** - * Returns the index of the first non null {@link ResourceQualifier} starting at index - * <var>startIndex</var> - * @param startIndex - * @return -1 if no qualifier was found. - */ - public int getHighestPriorityQualifier(int startIndex) { - for (int i = startIndex ; i < INDEX_COUNT ; i++) { - if (mQualifiers[i] != null) { - return i; - } - } - - return -1; - } - - /** - * Create default qualifiers. - * <p/>This creates qualifiers with no values for all indices. - */ - public void createDefault() { - mQualifiers[INDEX_COUNTRY_CODE] = new CountryCodeQualifier(); - mQualifiers[INDEX_NETWORK_CODE] = new NetworkCodeQualifier(); - mQualifiers[INDEX_LANGUAGE] = new LanguageQualifier(); - mQualifiers[INDEX_REGION] = new RegionQualifier(); - mQualifiers[INDEX_LAYOUTDIR] = new LayoutDirectionQualifier(); - mQualifiers[INDEX_SMALLEST_SCREEN_WIDTH] = new SmallestScreenWidthQualifier(); - mQualifiers[INDEX_SCREEN_WIDTH] = new ScreenWidthQualifier(); - mQualifiers[INDEX_SCREEN_HEIGHT] = new ScreenHeightQualifier(); - mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = new ScreenSizeQualifier(); - mQualifiers[INDEX_SCREEN_RATIO] = new ScreenRatioQualifier(); - mQualifiers[INDEX_SCREEN_ORIENTATION] = new ScreenOrientationQualifier(); - mQualifiers[INDEX_UI_MODE] = new UiModeQualifier(); - mQualifiers[INDEX_NIGHT_MODE] = new NightModeQualifier(); - mQualifiers[INDEX_PIXEL_DENSITY] = new DensityQualifier(); - mQualifiers[INDEX_TOUCH_TYPE] = new TouchScreenQualifier(); - mQualifiers[INDEX_KEYBOARD_STATE] = new KeyboardStateQualifier(); - mQualifiers[INDEX_TEXT_INPUT_METHOD] = new TextInputMethodQualifier(); - mQualifiers[INDEX_NAVIGATION_STATE] = new NavigationStateQualifier(); - mQualifiers[INDEX_NAVIGATION_METHOD] = new NavigationMethodQualifier(); - mQualifiers[INDEX_SCREEN_DIMENSION] = new ScreenDimensionQualifier(); - mQualifiers[INDEX_VERSION] = new VersionQualifier(); - } - - /** - * Returns an array of all the non null qualifiers. - */ - public ResourceQualifier[] getQualifiers() { - int count = 0; - for (int i = 0 ; i < INDEX_COUNT ; i++) { - if (mQualifiers[i] != null) { - count++; - } - } - - ResourceQualifier[] array = new ResourceQualifier[count]; - int index = 0; - for (int i = 0 ; i < INDEX_COUNT ; i++) { - if (mQualifiers[i] != null) { - array[index++] = mQualifiers[i]; - } - } - - return array; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/KeyboardStateQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/KeyboardStateQualifier.java deleted file mode 100644 index 1397808..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/KeyboardStateQualifier.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.resources.configuration; - -import com.android.resources.KeyboardState; -import com.android.resources.ResourceEnum; - -/** - * Resource Qualifier for keyboard state. - */ -public final class KeyboardStateQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "Keyboard State"; - - private KeyboardState mValue = null; - - public KeyboardStateQualifier() { - // pass - } - - public KeyboardStateQualifier(KeyboardState value) { - mValue = value; - } - - public KeyboardState getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return "Keyboard"; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - KeyboardState orientation = KeyboardState.getEnum(value); - if (orientation != null) { - KeyboardStateQualifier qualifier = new KeyboardStateQualifier(); - qualifier.mValue = orientation; - config.setKeyboardStateQualifier(qualifier); - return true; - } - - return false; - } - - @Override - public boolean isMatchFor(ResourceQualifier qualifier) { - if (qualifier instanceof KeyboardStateQualifier) { - KeyboardStateQualifier referenceQualifier = (KeyboardStateQualifier)qualifier; - - // special case where EXPOSED can be used for SOFT - if (referenceQualifier.mValue == KeyboardState.SOFT && - mValue == KeyboardState.EXPOSED) { - return true; - } - - return referenceQualifier.mValue == mValue; - } - - return false; - } - - @Override - public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) { - if (compareTo == null) { - return true; - } - - KeyboardStateQualifier compareQualifier = (KeyboardStateQualifier)compareTo; - KeyboardStateQualifier referenceQualifier = (KeyboardStateQualifier)reference; - - if (referenceQualifier.mValue == KeyboardState.SOFT) { // only case where there could be a - // better qualifier - // only return true if it's a better value. - if (compareQualifier.mValue == KeyboardState.EXPOSED && mValue == KeyboardState.SOFT) { - return true; - } - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/LanguageQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/LanguageQualifier.java deleted file mode 100644 index 76514e2..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/LanguageQualifier.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import java.util.Locale; -import java.util.regex.Pattern; - -/** - * Resource Qualifier for Language. - */ -public final class LanguageQualifier extends ResourceQualifier { - private final static Pattern sLanguagePattern = Pattern.compile("^[a-z]{2}$"); //$NON-NLS-1$ - - public static final String FAKE_LANG_VALUE = "__"; //$NON-NLS-1$ - public static final String NAME = "Language"; - - private String mValue; - - /** - * Creates and returns a qualifier from the given folder segment. If the segment is incorrect, - * <code>null</code> is returned. - * @param segment the folder segment from which to create a qualifier. - * @return a new {@link LanguageQualifier} object or <code>null</code> - */ - public static LanguageQualifier getQualifier(String segment) { - if (sLanguagePattern.matcher(segment).matches()) { - LanguageQualifier qualifier = new LanguageQualifier(); - qualifier.mValue = segment; - - return qualifier; - } - return null; - } - - /** - * Returns the folder name segment for the given value. This is equivalent to calling - * {@link #toString()} on a {@link LanguageQualifier} object. - * @param value the value of the qualifier, as returned by {@link #getValue()}. - */ - public static String getFolderSegment(String value) { - String segment = value.toLowerCase(Locale.US); - if (sLanguagePattern.matcher(segment).matches()) { - return segment; - } - - return null; - } - - public LanguageQualifier() { - - } - - public LanguageQualifier(String value) { - mValue = value; - } - - public String getValue() { - if (mValue != null) { - return mValue; - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean isValid() { - return mValue != null; - } - - @Override - public boolean hasFakeValue() { - return FAKE_LANG_VALUE.equals(mValue); - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - LanguageQualifier qualifier = getQualifier(value); - if (qualifier != null) { - config.setLanguageQualifier(qualifier); - return true; - } - - return false; - } - - @Override - public boolean equals(Object qualifier) { - if (qualifier instanceof LanguageQualifier) { - if (mValue == null) { - return ((LanguageQualifier)qualifier).mValue == null; - } - return mValue.equals(((LanguageQualifier)qualifier).mValue); - } - - return false; - } - - @Override - public int hashCode() { - if (mValue != null) { - return mValue.hashCode(); - } - - return 0; - } - - /** - * Returns the string used to represent this qualifier in the folder name. - */ - @Override - public String getFolderSegment() { - if (mValue != null) { - return getFolderSegment(mValue); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getShortDisplayValue() { - if (mValue != null) { - return mValue; - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getLongDisplayValue() { - if (mValue != null) { - return String.format("Language %s", mValue); - } - - return ""; //$NON-NLS-1$ - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/LayoutDirectionQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/LayoutDirectionQualifier.java deleted file mode 100644 index 48498f0..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/LayoutDirectionQualifier.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.ide.common.resources.configuration; - -import com.android.resources.LayoutDirection; -import com.android.resources.ResourceEnum; - -/** - * Resource Qualifier for layout direction. values can be "ltr", or "rtl" - */ -public class LayoutDirectionQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "Layout Direction"; - - private LayoutDirection mValue = null; - - - public LayoutDirectionQualifier() { - } - - public LayoutDirectionQualifier(LayoutDirection value) { - mValue = value; - } - - public LayoutDirection getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 17; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - LayoutDirection ld = LayoutDirection.getEnum(value); - if (ld != null) { - LayoutDirectionQualifier qualifier = new LayoutDirectionQualifier(ld); - config.setLayoutDirectionQualifier(qualifier); - return true; - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/NavigationMethodQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/NavigationMethodQualifier.java deleted file mode 100644 index f40bc6c..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/NavigationMethodQualifier.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.resources.Navigation; -import com.android.resources.ResourceEnum; - -/** - * Resource Qualifier for Navigation Method. - */ -public final class NavigationMethodQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "Navigation Method"; - - private Navigation mValue; - - public NavigationMethodQualifier() { - // pass - } - - public NavigationMethodQualifier(Navigation value) { - mValue = value; - } - - public Navigation getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - Navigation method = Navigation.getEnum(value); - if (method != null) { - NavigationMethodQualifier qualifier = new NavigationMethodQualifier(method); - config.setNavigationMethodQualifier(qualifier); - return true; - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/NavigationStateQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/NavigationStateQualifier.java deleted file mode 100644 index 91b81df..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/NavigationStateQualifier.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.resources.configuration; - -import com.android.resources.NavigationState; -import com.android.resources.ResourceEnum; - -/** - * Resource Qualifier for navigation state. - */ -public final class NavigationStateQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "Navigation State"; - - private NavigationState mValue = null; - - public NavigationStateQualifier() { - // pass - } - - public NavigationStateQualifier(NavigationState value) { - mValue = value; - } - - public NavigationState getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - NavigationState state = NavigationState.getEnum(value); - if (state != null) { - NavigationStateQualifier qualifier = new NavigationStateQualifier(); - qualifier.mValue = state; - config.setNavigationStateQualifier(qualifier); - return true; - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/NetworkCodeQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/NetworkCodeQualifier.java deleted file mode 100644 index 1ef2015..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/NetworkCodeQualifier.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.resources.configuration; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Resource Qualifier for Mobile Network Code Pixel Density. - */ -public final class NetworkCodeQualifier extends ResourceQualifier { - /** Default pixel density value. This means the property is not set. */ - private final static int DEFAULT_CODE = -1; - - private final static Pattern sNetworkCodePattern = Pattern.compile("^mnc(\\d{1,3})$"); //$NON-NLS-1$ - - private final int mCode; - - public final static String NAME = "Mobile Network Code"; - - /** - * Creates and returns a qualifier from the given folder segment. If the segment is incorrect, - * <code>null</code> is returned. - * @param segment the folder segment from which to create a qualifier. - * @return a new {@link CountryCodeQualifier} object or <code>null</code> - */ - public static NetworkCodeQualifier getQualifier(String segment) { - Matcher m = sNetworkCodePattern.matcher(segment); - if (m.matches()) { - String v = m.group(1); - - int code = -1; - try { - code = Integer.parseInt(v); - } catch (NumberFormatException e) { - // looks like the string we extracted wasn't a valid number. - return null; - } - - NetworkCodeQualifier qualifier = new NetworkCodeQualifier(code); - return qualifier; - } - - return null; - } - - /** - * Returns the folder name segment for the given value. This is equivalent to calling - * {@link #toString()} on a {@link NetworkCodeQualifier} object. - * @param code the value of the qualifier, as returned by {@link #getCode()}. - */ - public static String getFolderSegment(int code) { - if (code != DEFAULT_CODE && code >= 1 && code <= 999) { // code is 1-3 digit. - return String.format("mnc%1$d", code); //$NON-NLS-1$ - } - - return ""; //$NON-NLS-1$ - } - - public NetworkCodeQualifier() { - this(DEFAULT_CODE); - } - - public NetworkCodeQualifier(int code) { - mCode = code; - } - - public int getCode() { - return mCode; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return "Network Code"; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean isValid() { - return mCode != DEFAULT_CODE; - } - - @Override - public boolean hasFakeValue() { - return false; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - Matcher m = sNetworkCodePattern.matcher(value); - if (m.matches()) { - String v = m.group(1); - - int code = -1; - try { - code = Integer.parseInt(v); - } catch (NumberFormatException e) { - // looks like the string we extracted wasn't a valid number. - return false; - } - - NetworkCodeQualifier qualifier = new NetworkCodeQualifier(code); - config.setNetworkCodeQualifier(qualifier); - return true; - } - - return false; - } - - @Override - public boolean equals(Object qualifier) { - if (qualifier instanceof NetworkCodeQualifier) { - return mCode == ((NetworkCodeQualifier)qualifier).mCode; - } - - return false; - } - - @Override - public int hashCode() { - return mCode; - } - - /** - * Returns the string used to represent this qualifier in the folder name. - */ - @Override - public String getFolderSegment() { - return getFolderSegment(mCode); - } - - @Override - public String getShortDisplayValue() { - if (mCode != DEFAULT_CODE) { - return String.format("MNC %1$d", mCode); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getLongDisplayValue() { - return getShortDisplayValue(); - } - -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/NightModeQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/NightModeQualifier.java deleted file mode 100644 index d3b6760..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/NightModeQualifier.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.resources.configuration; - -import com.android.resources.NightMode; -import com.android.resources.ResourceEnum; - -/** - * Resource Qualifier for Navigation Method. - */ -public final class NightModeQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "Night Mode"; - - private NightMode mValue; - - public NightModeQualifier() { - // pass - } - - public NightModeQualifier(NightMode value) { - mValue = value; - } - - public NightMode getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return "Night Mode"; - } - - @Override - public int since() { - return 8; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - NightMode mode = NightMode.getEnum(value); - if (mode != null) { - NightModeQualifier qualifier = new NightModeQualifier(mode); - config.setNightModeQualifier(qualifier); - return true; - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/RegionQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/RegionQualifier.java deleted file mode 100644 index bd033bd..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/RegionQualifier.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Resource Qualifier for Region. - */ -public final class RegionQualifier extends ResourceQualifier { - private final static Pattern sRegionPattern = Pattern.compile("^r([A-Z]{2})$"); //$NON-NLS-1$ - - public static final String FAKE_REGION_VALUE = "__"; //$NON-NLS-1$ - public static final String NAME = "Region"; - - private String mValue; - - /** - * Creates and returns a qualifier from the given folder segment. If the segment is incorrect, - * <code>null</code> is returned. - * @param segment the folder segment from which to create a qualifier. - * @return a new {@link RegionQualifier} object or <code>null</code> - */ - public static RegionQualifier getQualifier(String segment) { - Matcher m = sRegionPattern.matcher(segment); - if (m.matches()) { - RegionQualifier qualifier = new RegionQualifier(); - qualifier.mValue = m.group(1); - - return qualifier; - } - return null; - } - - /** - * Returns the folder name segment for the given value. This is equivalent to calling - * {@link #toString()} on a {@link RegionQualifier} object. - * @param value the value of the qualifier, as returned by {@link #getValue()}. - */ - public static String getFolderSegment(String value) { - if (value != null) { - // See http://developer.android.com/reference/java/util/Locale.html#default_locale - String segment = "r" + value.toUpperCase(Locale.US); //$NON-NLS-1$ - if (sRegionPattern.matcher(segment).matches()) { - return segment; - } - } - - return ""; //$NON-NLS-1$ - } - - public RegionQualifier() { - - } - - public RegionQualifier(String value) { - mValue = value; - } - - public String getValue() { - if (mValue != null) { - return mValue; - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean isValid() { - return mValue != null; - } - - @Override - public boolean hasFakeValue() { - return FAKE_REGION_VALUE.equals(mValue); - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - RegionQualifier qualifier = getQualifier(value); - if (qualifier != null) { - config.setRegionQualifier(qualifier); - return true; - } - - return false; - } - - @Override - public boolean equals(Object qualifier) { - if (qualifier instanceof RegionQualifier) { - if (mValue == null) { - return ((RegionQualifier)qualifier).mValue == null; - } - return mValue.equals(((RegionQualifier)qualifier).mValue); - } - - return false; - } - - @Override - public int hashCode() { - if (mValue != null) { - return mValue.hashCode(); - } - - return 0; - } - - /** - * Returns the string used to represent this qualifier in the folder name. - */ - @Override - public String getFolderSegment() { - return getFolderSegment(mValue); - } - - @Override - public String getShortDisplayValue() { - if (mValue != null) { - return mValue; - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getLongDisplayValue() { - if (mValue != null) { - return String.format("Region %s", mValue); - } - - return ""; //$NON-NLS-1$ - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/ResourceQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/ResourceQualifier.java deleted file mode 100644 index 2997c8f..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/ResourceQualifier.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - - -/** - * Base class for resource qualifiers. - * <p/>The resource qualifier classes are designed as immutable. - */ -public abstract class ResourceQualifier implements Comparable<ResourceQualifier> { - - /** - * Returns the human readable name of the qualifier. - */ - public abstract String getName(); - - /** - * Returns a shorter human readable name for the qualifier. - * @see #getName() - */ - public abstract String getShortName(); - - /** - * Returns when this qualifier was added to Android. - */ - public abstract int since(); - - /** - * Whether this qualifier is deprecated. - */ - public boolean deprecated() { - return false; - } - - /** - * Returns whether the qualifier has a valid filter value. - */ - public abstract boolean isValid(); - - /** - * Returns whether the qualifier has a fake value. - * <p/>Fake values are used internally and should not be used as real qualifier value. - */ - public abstract boolean hasFakeValue(); - - /** - * Check if the value is valid for this qualifier, and if so sets the value - * into a Folder Configuration. - * @param value The value to check and set. Must not be null. - * @param config The folder configuration to receive the value. Must not be null. - * @return true if the value was valid and was set. - */ - public abstract boolean checkAndSet(String value, FolderConfiguration config); - - /** - * Returns a string formated to be used in a folder name. - * <p/>This is declared as abstract to force children classes to implement it. - */ - public abstract String getFolderSegment(); - - /** - * Returns whether the given qualifier is a match for the receiver. - * <p/>The default implementation returns the result of {@link #equals(Object)}. - * <p/>Children class that re-implements this must implement - * {@link #isBetterMatchThan(ResourceQualifier, ResourceQualifier)} too. - * @param qualifier the reference qualifier - * @return true if the receiver is a match. - */ - public boolean isMatchFor(ResourceQualifier qualifier) { - return equals(qualifier); - } - - /** - * Returns true if the receiver is a better match for the given <var>reference</var> than - * the given <var>compareTo</var> comparable. - * @param compareTo The {@link ResourceQualifier} to compare to. Can be null, in which - * case the method must return <code>true</code>. - * @param reference The reference qualifier value for which the match is. - * @return true if the receiver is a better match. - */ - public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) { - // the default is to always return false. This gives less overhead than always returning - // true, as it would only compare same values anyway. - return false; - } - - @Override - public String toString() { - return getFolderSegment(); - } - - /** - * Returns a string formatted for display purpose. - */ - public abstract String getShortDisplayValue(); - - /** - * Returns a string formatted for display purpose. - */ - public abstract String getLongDisplayValue(); - - /** - * Returns <code>true</code> if both objects are equal. - * <p/>This is declared as abstract to force children classes to implement it. - */ - @Override - public abstract boolean equals(Object object); - - /** - * Returns a hash code value for the object. - * <p/>This is declared as abstract to force children classes to implement it. - */ - @Override - public abstract int hashCode(); - - @Override - public final int compareTo(ResourceQualifier o) { - return toString().compareTo(o.toString()); - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifier.java deleted file mode 100644 index dce6c68..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifier.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Resource Qualifier for Screen Dimension. - */ -public final class ScreenDimensionQualifier extends ResourceQualifier { - /** Default screen size value. This means the property is not set */ - final static int DEFAULT_SIZE = -1; - - private final static Pattern sDimensionPattern = Pattern.compile( - "^(\\d+)x(\\d+)$"); //$NON-NLS-1$ - - public static final String NAME = "Screen Dimension"; - - /** Screen size 1 value. This is not size X or Y because the folder name always - * contains the biggest size first. So if the qualifier is 400x200, size 1 will always be - * 400 but that'll be X in landscape and Y in portrait. - * Default value is <code>DEFAULT_SIZE</code> */ - private int mValue1 = DEFAULT_SIZE; - - /** Screen size 2 value. This is not size X or Y because the folder name always - * contains the biggest size first. So if the qualifier is 400x200, size 2 will always be - * 200 but that'll be Y in landscape and X in portrait. - * Default value is <code>DEFAULT_SIZE</code> */ - private int mValue2 = DEFAULT_SIZE; - - public ScreenDimensionQualifier() { - // pass - } - - public ScreenDimensionQualifier(int value1, int value2) { - mValue1 = value1; - mValue2 = value2; - } - - public int getValue1() { - return mValue1; - } - - public int getValue2() { - return mValue2; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return "Dimension"; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean deprecated() { - return true; - } - - @Override - public boolean isValid() { - return mValue1 != DEFAULT_SIZE && mValue2 != DEFAULT_SIZE; - } - - @Override - public boolean hasFakeValue() { - return false; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - Matcher m = sDimensionPattern.matcher(value); - if (m.matches()) { - String d1 = m.group(1); - String d2 = m.group(2); - - ScreenDimensionQualifier qualifier = getQualifier(d1, d2); - if (qualifier != null) { - config.setScreenDimensionQualifier(qualifier); - return true; - } - } - return false; - } - - @Override - public boolean equals(Object qualifier) { - if (qualifier instanceof ScreenDimensionQualifier) { - ScreenDimensionQualifier q = (ScreenDimensionQualifier)qualifier; - return (mValue1 == q.mValue1 && mValue2 == q.mValue2); - } - - return false; - } - - @Override - public int hashCode() { - return toString().hashCode(); - } - - public static ScreenDimensionQualifier getQualifier(String size1, String size2) { - try { - int s1 = Integer.parseInt(size1); - int s2 = Integer.parseInt(size2); - - ScreenDimensionQualifier qualifier = new ScreenDimensionQualifier(); - - if (s1 > s2) { - qualifier.mValue1 = s1; - qualifier.mValue2 = s2; - } else { - qualifier.mValue1 = s2; - qualifier.mValue2 = s1; - } - - return qualifier; - } catch (NumberFormatException e) { - // looks like the string we extracted wasn't a valid number. - } - - return null; - } - - /** - * Returns the string used to represent this qualifier in the folder name. - */ - @Override - public String getFolderSegment() { - return String.format("%1$dx%2$d", mValue1, mValue2); //$NON-NLS-1$ - } - - @Override - public String getShortDisplayValue() { - if (isValid()) { - return String.format("%1$dx%2$d", mValue1, mValue2); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getLongDisplayValue() { - if (isValid()) { - return String.format("Screen resolution %1$dx%2$d", mValue1, mValue2); - } - - return ""; //$NON-NLS-1$ - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenHeightQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/ScreenHeightQualifier.java deleted file mode 100644 index 08bba61..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenHeightQualifier.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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.ide.common.resources.configuration; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Resource Qualifier for Screen Pixel Density. - */ -public final class ScreenHeightQualifier extends ResourceQualifier { - /** Default screen size value. This means the property is not set */ - final static int DEFAULT_SIZE = -1; - - private final static Pattern sParsePattern = Pattern.compile("^h(\\d+)dp$");//$NON-NLS-1$ - private final static String sPrintPattern = "h%1$ddp"; - - public static final String NAME = "Screen Height"; - - private int mValue = DEFAULT_SIZE; - - public ScreenHeightQualifier() { - // pass - } - - public ScreenHeightQualifier(int value) { - mValue = value; - } - - public int getValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 13; - } - - @Override - public boolean hasFakeValue() { - return false; - } - - @Override - public boolean isValid() { - return mValue != DEFAULT_SIZE; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - Matcher m = sParsePattern.matcher(value); - if (m.matches()) { - String v = m.group(1); - - ScreenHeightQualifier qualifier = getQualifier(v); - if (qualifier != null) { - config.setScreenHeightQualifier(qualifier); - return true; - } - } - - return false; - } - - public static ScreenHeightQualifier getQualifier(String value) { - try { - int dp = Integer.parseInt(value); - - ScreenHeightQualifier qualifier = new ScreenHeightQualifier(); - qualifier.mValue = dp; - return qualifier; - - } catch (NumberFormatException e) { - } - - return null; - } - - @Override - public boolean isMatchFor(ResourceQualifier qualifier) { - // this is the match only of the current dp value is lower or equal to the - if (qualifier instanceof ScreenHeightQualifier) { - return mValue <= ((ScreenHeightQualifier) qualifier).mValue; - } - - return false; - } - - @Override - public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) { - if (compareTo == null) { - return true; - } - - ScreenHeightQualifier compareQ = (ScreenHeightQualifier)compareTo; - ScreenHeightQualifier referenceQ = (ScreenHeightQualifier)reference; - - if (compareQ.mValue == referenceQ.mValue) { - // what we have is already the best possible match (exact match) - return false; - } else if (mValue == referenceQ.mValue) { - // got new exact value, this is the best! - return true; - } else { - // get the qualifier that has the width that is the closest to the reference, but not - // above. (which is guaranteed when this is called as isMatchFor is called first. - return mValue > compareQ.mValue; - } - } - - @Override - public String getFolderSegment() { - return String.format(sPrintPattern, mValue); - } - - @Override - public String getShortDisplayValue() { - if (isValid()) { - return getFolderSegment(); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getLongDisplayValue() { - if (isValid()) { - return getFolderSegment(); - } - - return ""; //$NON-NLS-1$ - } - - - @Override - public int hashCode() { - return mValue; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ScreenHeightQualifier other = (ScreenHeightQualifier) obj; - if (mValue != other.mValue) { - return false; - } - return true; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifier.java deleted file mode 100644 index 732a078..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifier.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.resources.ResourceEnum; -import com.android.resources.ScreenOrientation; - -/** - * Resource Qualifier for Screen Orientation. - */ -public final class ScreenOrientationQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "Screen Orientation"; - - private ScreenOrientation mValue = null; - - public ScreenOrientationQualifier() { - } - - public ScreenOrientationQualifier(ScreenOrientation value) { - mValue = value; - } - - public ScreenOrientation getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return "Orientation"; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - ScreenOrientation orientation = ScreenOrientation.getEnum(value); - if (orientation != null) { - ScreenOrientationQualifier qualifier = new ScreenOrientationQualifier(orientation); - config.setScreenOrientationQualifier(qualifier); - return true; - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenRatioQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/ScreenRatioQualifier.java deleted file mode 100644 index b45946b..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenRatioQualifier.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2009 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.ide.common.resources.configuration; - -import com.android.resources.ResourceEnum; -import com.android.resources.ScreenRatio; - -public class ScreenRatioQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "Screen Ratio"; - - private ScreenRatio mValue = null; - - public ScreenRatioQualifier() { - } - - public ScreenRatioQualifier(ScreenRatio value) { - mValue = value; - } - - public ScreenRatio getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return "Ratio"; - } - - @Override - public int since() { - return 4; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - ScreenRatio size = ScreenRatio.getEnum(value); - if (size != null) { - ScreenRatioQualifier qualifier = new ScreenRatioQualifier(size); - config.setScreenRatioQualifier(qualifier); - return true; - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenSizeQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/ScreenSizeQualifier.java deleted file mode 100644 index 77193a2..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenSizeQualifier.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2009 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.ide.common.resources.configuration; - -import com.android.resources.ResourceEnum; -import com.android.resources.ScreenSize; - -/** - * Resource Qualifier for Screen Size. Size can be "small", "normal", "large" and "x-large" - */ -public class ScreenSizeQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "Screen Size"; - - private ScreenSize mValue = null; - - - public ScreenSizeQualifier() { - } - - public ScreenSizeQualifier(ScreenSize value) { - mValue = value; - } - - public ScreenSize getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return "Size"; - } - - @Override - public int since() { - return 4; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - ScreenSize size = ScreenSize.getEnum(value); - if (size != null) { - ScreenSizeQualifier qualifier = new ScreenSizeQualifier(size); - config.setScreenSizeQualifier(qualifier); - return true; - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenWidthQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/ScreenWidthQualifier.java deleted file mode 100644 index ab9134b..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/ScreenWidthQualifier.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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.ide.common.resources.configuration; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Resource Qualifier for Screen Pixel Density. - */ -public final class ScreenWidthQualifier extends ResourceQualifier { - /** Default screen size value. This means the property is not set */ - final static int DEFAULT_SIZE = -1; - - private final static Pattern sParsePattern = Pattern.compile("^w(\\d+)dp$"); //$NON-NLS-1$ - private final static String sPrintPattern = "w%1$ddp"; //$NON-NLS-1$ - - public static final String NAME = "Screen Width"; - - private int mValue = DEFAULT_SIZE; - - public ScreenWidthQualifier() { - // pass - } - - public ScreenWidthQualifier(int value) { - mValue = value; - } - - public int getValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 13; - } - - @Override - public boolean hasFakeValue() { - return false; - } - - @Override - public boolean isValid() { - return mValue != DEFAULT_SIZE; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - Matcher m = sParsePattern.matcher(value); - if (m.matches()) { - String v = m.group(1); - - ScreenWidthQualifier qualifier = getQualifier(v); - if (qualifier != null) { - config.setScreenWidthQualifier(qualifier); - return true; - } - } - - return false; - } - - public static ScreenWidthQualifier getQualifier(String value) { - try { - int dp = Integer.parseInt(value); - - ScreenWidthQualifier qualifier = new ScreenWidthQualifier(); - qualifier.mValue = dp; - return qualifier; - - } catch (NumberFormatException e) { - } - - return null; - } - - @Override - public boolean isMatchFor(ResourceQualifier qualifier) { - // this is the match only of the current dp value is lower or equal to the - if (qualifier instanceof ScreenWidthQualifier) { - return mValue <= ((ScreenWidthQualifier) qualifier).mValue; - } - - return false; - } - - @Override - public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) { - if (compareTo == null) { - return true; - } - - ScreenWidthQualifier compareQ = (ScreenWidthQualifier)compareTo; - ScreenWidthQualifier referenceQ = (ScreenWidthQualifier)reference; - - if (compareQ.mValue == referenceQ.mValue) { - // what we have is already the best possible match (exact match) - return false; - } else if (mValue == referenceQ.mValue) { - // got new exact value, this is the best! - return true; - } else { - // get the qualifier that has the width that is the closest to the reference, but not - // above. (which is guaranteed when this is called as isMatchFor is called first. - return mValue > compareQ.mValue; - } - } - - @Override - public String getFolderSegment() { - return String.format(sPrintPattern, mValue); - } - - @Override - public String getShortDisplayValue() { - if (isValid()) { - return getFolderSegment(); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getLongDisplayValue() { - if (isValid()) { - return getFolderSegment(); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public int hashCode() { - return mValue; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ScreenWidthQualifier other = (ScreenWidthQualifier) obj; - if (mValue != other.mValue) { - return false; - } - return true; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/SmallestScreenWidthQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/SmallestScreenWidthQualifier.java deleted file mode 100644 index 35d1ab1..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/SmallestScreenWidthQualifier.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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.ide.common.resources.configuration; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Resource Qualifier for Screen Pixel Density. - */ -public final class SmallestScreenWidthQualifier extends ResourceQualifier { - /** Default screen size value. This means the property is not set */ - final static int DEFAULT_SIZE = -1; - - private final static Pattern sParsePattern = Pattern.compile("^sw(\\d+)dp$"); //$NON-NLS-1$ - private final static String sPrintPattern = "sw%1$ddp"; //$NON-NLS-1$ - - public static final String NAME = "Smallest Screen Width"; - - private int mValue = DEFAULT_SIZE; - - public SmallestScreenWidthQualifier() { - // pass - } - - public SmallestScreenWidthQualifier(int value) { - mValue = value; - } - - public int getValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 13; - } - - @Override - public boolean hasFakeValue() { - return false; - } - - @Override - public boolean isValid() { - return mValue != DEFAULT_SIZE; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - Matcher m = sParsePattern.matcher(value); - if (m.matches()) { - String v = m.group(1); - - SmallestScreenWidthQualifier qualifier = getQualifier(v); - if (qualifier != null) { - config.setSmallestScreenWidthQualifier(qualifier); - return true; - } - } - - return false; - } - - public static SmallestScreenWidthQualifier getQualifier(String value) { - try { - int dp = Integer.parseInt(value); - - SmallestScreenWidthQualifier qualifier = new SmallestScreenWidthQualifier(); - qualifier.mValue = dp; - return qualifier; - - } catch (NumberFormatException e) { - } - - return null; - } - - @Override - public boolean isMatchFor(ResourceQualifier qualifier) { - // this is the match only of the current dp value is lower or equal to the - if (qualifier instanceof SmallestScreenWidthQualifier) { - return mValue <= ((SmallestScreenWidthQualifier) qualifier).mValue; - } - - return false; - } - - @Override - public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) { - if (compareTo == null) { - return true; - } - - SmallestScreenWidthQualifier compareQ = (SmallestScreenWidthQualifier)compareTo; - SmallestScreenWidthQualifier referenceQ = (SmallestScreenWidthQualifier)reference; - - if (compareQ.mValue == referenceQ.mValue) { - // what we have is already the best possible match (exact match) - return false; - } else if (mValue == referenceQ.mValue) { - // got new exact value, this is the best! - return true; - } else { - // get the qualifier that has the width that is the closest to the reference, but not - // above. (which is guaranteed when this is called as isMatchFor is called first. - return mValue > compareQ.mValue; - } - } - - @Override - public String getFolderSegment() { - return String.format(sPrintPattern, mValue); - } - - @Override - public String getShortDisplayValue() { - if (isValid()) { - return getFolderSegment(); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getLongDisplayValue() { - if (isValid()) { - return getFolderSegment(); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public int hashCode() { - return mValue; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - SmallestScreenWidthQualifier other = (SmallestScreenWidthQualifier) obj; - if (mValue != other.mValue) { - return false; - } - return true; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/TextInputMethodQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/TextInputMethodQualifier.java deleted file mode 100644 index 784d43d..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/TextInputMethodQualifier.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.resources.Keyboard; -import com.android.resources.ResourceEnum; - -/** - * Resource Qualifier for Text Input Method. - */ -public final class TextInputMethodQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "Text Input Method"; - - private Keyboard mValue; - - - public TextInputMethodQualifier() { - // pass - } - - public TextInputMethodQualifier(Keyboard value) { - mValue = value; - } - - public Keyboard getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return "Text Input"; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - Keyboard method = Keyboard.getEnum(value); - if (method != null) { - TextInputMethodQualifier qualifier = new TextInputMethodQualifier(); - qualifier.mValue = method; - config.setTextInputMethodQualifier(qualifier); - return true; - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/TouchScreenQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/TouchScreenQualifier.java deleted file mode 100644 index dce9f1d..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/TouchScreenQualifier.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.resources.ResourceEnum; -import com.android.resources.TouchScreen; - - -/** - * Resource Qualifier for Touch Screen type. - */ -public final class TouchScreenQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "Touch Screen"; - - private TouchScreen mValue; - - public TouchScreenQualifier() { - // pass - } - - public TouchScreenQualifier(TouchScreen touchValue) { - mValue = touchValue; - } - - public TouchScreen getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - TouchScreen type = TouchScreen.getEnum(value); - if (type != null) { - TouchScreenQualifier qualifier = new TouchScreenQualifier(); - qualifier.mValue = type; - config.setTouchTypeQualifier(qualifier); - return true; - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/UiModeQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/UiModeQualifier.java deleted file mode 100644 index 1e302c5..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/UiModeQualifier.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.resources.configuration; - -import com.android.resources.ResourceEnum; -import com.android.resources.UiMode; - -/** - * Resource Qualifier for UI Mode. - */ -public final class UiModeQualifier extends EnumBasedResourceQualifier { - - public static final String NAME = "UI Mode"; - - private UiMode mValue; - - public UiModeQualifier() { - // pass - } - - public UiModeQualifier(UiMode value) { - mValue = value; - } - - public UiMode getValue() { - return mValue; - } - - @Override - ResourceEnum getEnumValue() { - return mValue; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return NAME; - } - - @Override - public int since() { - return 8; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - UiMode mode = UiMode.getEnum(value); - if (mode != null) { - UiModeQualifier qualifier = new UiModeQualifier(mode); - config.setUiModeQualifier(qualifier); - return true; - } - - return false; - } - - @Override - public boolean isMatchFor(ResourceQualifier qualifier) { - // only normal is a match for all UI mode, because it's not an actual mode. - if (mValue == UiMode.NORMAL) { - return true; - } - - // others must be an exact match - return ((UiModeQualifier)qualifier).mValue == mValue; - } - - @Override - public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) { - if (compareTo == null) { - return true; - } - - UiModeQualifier compareQualifier = (UiModeQualifier)compareTo; - UiModeQualifier referenceQualifier = (UiModeQualifier)reference; - - if (compareQualifier.getValue() == referenceQualifier.getValue()) { - // what we have is already the best possible match (exact match) - return false; - } else if (mValue == referenceQualifier.mValue) { - // got new exact value, this is the best! - return true; - } else if (mValue == UiMode.NORMAL) { - // else "normal" can be a match in case there's no exact match - return true; - } - - return false; - } -} diff --git a/sdk_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java b/sdk_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java deleted file mode 100644 index 078d4af..0000000 --- a/sdk_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2009 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.ide.common.resources.configuration; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Resource Qualifier for Platform Version. - */ -public final class VersionQualifier extends ResourceQualifier { - /** Default pixel density value. This means the property is not set. */ - private final static int DEFAULT_VERSION = -1; - - private final static Pattern sVersionPattern = Pattern.compile("^v(\\d+)$");//$NON-NLS-1$ - - private int mVersion = DEFAULT_VERSION; - - public static final String NAME = "Platform Version"; - - /** - * Creates and returns a qualifier from the given folder segment. If the segment is incorrect, - * <code>null</code> is returned. - * @param segment the folder segment from which to create a qualifier. - * @return a new {@link VersionQualifier} object or <code>null</code> - */ - public static VersionQualifier getQualifier(String segment) { - Matcher m = sVersionPattern.matcher(segment); - if (m.matches()) { - String v = m.group(1); - - int code = -1; - try { - code = Integer.parseInt(v); - } catch (NumberFormatException e) { - // looks like the string we extracted wasn't a valid number. - return null; - } - - VersionQualifier qualifier = new VersionQualifier(); - qualifier.mVersion = code; - return qualifier; - } - - return null; - } - - /** - * Returns the folder name segment for the given value. This is equivalent to calling - * {@link #toString()} on a {@link VersionQualifier} object. - * @param version the value of the qualifier, as returned by {@link #getVersion()}. - */ - public static String getFolderSegment(int version) { - if (version != DEFAULT_VERSION) { - return String.format("v%1$d", version); //$NON-NLS-1$ - } - - return ""; //$NON-NLS-1$ - } - - public VersionQualifier(int apiLevel) { - mVersion = apiLevel; - } - - public VersionQualifier() { - //pass - } - - public int getVersion() { - return mVersion; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public String getShortName() { - return "Version"; - } - - @Override - public int since() { - return 1; - } - - @Override - public boolean isValid() { - return mVersion != DEFAULT_VERSION; - } - - @Override - public boolean hasFakeValue() { - return false; - } - - @Override - public boolean checkAndSet(String value, FolderConfiguration config) { - VersionQualifier qualifier = getQualifier(value); - if (qualifier != null) { - config.setVersionQualifier(qualifier); - return true; - } - - return false; - } - - @Override - public boolean equals(Object qualifier) { - if (qualifier instanceof VersionQualifier) { - return mVersion == ((VersionQualifier)qualifier).mVersion; - } - - return false; - } - - @Override - public boolean isMatchFor(ResourceQualifier qualifier) { - if (qualifier instanceof VersionQualifier) { - // it is considered a match if the api level is equal or lower to the given qualifier - return mVersion <= ((VersionQualifier) qualifier).mVersion; - } - - return false; - } - - @Override - public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) { - if (compareTo == null) { - return true; - } - - VersionQualifier compareQ = (VersionQualifier)compareTo; - VersionQualifier referenceQ = (VersionQualifier)reference; - - if (compareQ.mVersion == referenceQ.mVersion) { - // what we have is already the best possible match (exact match) - return false; - } else if (mVersion == referenceQ.mVersion) { - // got new exact value, this is the best! - return true; - } else { - // in all case we're going to prefer the higher version (since they have been filtered - // to not be too high - return mVersion > compareQ.mVersion; - } - } - - @Override - public int hashCode() { - return mVersion; - } - - /** - * Returns the string used to represent this qualifier in the folder name. - */ - @Override - public String getFolderSegment() { - return getFolderSegment(mVersion); - } - - @Override - public String getShortDisplayValue() { - if (mVersion != DEFAULT_VERSION) { - return String.format("API %1$d", mVersion); - } - - return ""; //$NON-NLS-1$ - } - - @Override - public String getLongDisplayValue() { - if (mVersion != DEFAULT_VERSION) { - return String.format("API Level %1$d", mVersion); - } - - return ""; //$NON-NLS-1$ - } -} diff --git a/sdk_common/src/com/android/ide/common/sdk/LoadStatus.java b/sdk_common/src/com/android/ide/common/sdk/LoadStatus.java deleted file mode 100644 index babbd63..0000000 --- a/sdk_common/src/com/android/ide/common/sdk/LoadStatus.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.sdk; - -/** - * Enum for loading status of various SDK parts. - */ -public enum LoadStatus { - LOADING, LOADED, FAILED; -} diff --git a/sdk_common/src/com/android/ide/common/xml/AndroidManifestParser.java b/sdk_common/src/com/android/ide/common/xml/AndroidManifestParser.java deleted file mode 100644 index 7fde6ce..0000000 --- a/sdk_common/src/com/android/ide/common/xml/AndroidManifestParser.java +++ /dev/null @@ -1,672 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.xml; - -import com.android.SdkConstants; -import com.android.ide.common.xml.ManifestData.Activity; -import com.android.ide.common.xml.ManifestData.Instrumentation; -import com.android.ide.common.xml.ManifestData.SupportsScreens; -import com.android.ide.common.xml.ManifestData.UsesConfiguration; -import com.android.ide.common.xml.ManifestData.UsesFeature; -import com.android.ide.common.xml.ManifestData.UsesLibrary; -import com.android.io.IAbstractFile; -import com.android.io.IAbstractFolder; -import com.android.io.StreamException; -import com.android.resources.Keyboard; -import com.android.resources.Navigation; -import com.android.resources.TouchScreen; -import com.android.xml.AndroidManifest; - -import org.xml.sax.Attributes; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.Locator; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.helpers.DefaultHandler; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Locale; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -public class AndroidManifestParser { - - private final static int LEVEL_TOP = 0; - private final static int LEVEL_INSIDE_MANIFEST = 1; - private final static int LEVEL_INSIDE_APPLICATION = 2; - private final static int LEVEL_INSIDE_APP_COMPONENT = 3; - private final static int LEVEL_INSIDE_INTENT_FILTER = 4; - - private final static String ACTION_MAIN = "android.intent.action.MAIN"; //$NON-NLS-1$ - private final static String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; //$NON-NLS-1$ - - public interface ManifestErrorHandler extends ErrorHandler { - /** - * Handles a parsing error and an optional line number. - */ - void handleError(Exception exception, int lineNumber); - - /** - * Checks that a class is valid and can be used in the Android Manifest. - * <p/> - * Errors are put as {@code org.eclipse.core.resources.IMarker} on the manifest file. - * - * @param locator - * @param className the fully qualified name of the class to test. - * @param superClassName the fully qualified name of the class it is supposed to extend. - * @param testVisibility if <code>true</code>, the method will check the visibility of - * the class or of its constructors. - */ - void checkClass(Locator locator, String className, String superClassName, - boolean testVisibility); - } - - /** - * XML error & data handler used when parsing the AndroidManifest.xml file. - * <p/> - * During parsing this will fill up the {@link ManifestData} object given to the constructor - * and call out errors to the given {@link ManifestErrorHandler}. - */ - private static class ManifestHandler extends DefaultHandler { - - //--- temporary data/flags used during parsing - private final ManifestData mManifestData; - private final ManifestErrorHandler mErrorHandler; - private int mCurrentLevel = 0; - private int mValidLevel = 0; - private Activity mCurrentActivity = null; - private Locator mLocator; - - /** - * Creates a new {@link ManifestHandler}. - * - * @param manifestFile The manifest file being parsed. Can be null. - * @param manifestData Class containing the manifest info obtained during the parsing. - * @param errorHandler An optional error handler. - */ - ManifestHandler(IAbstractFile manifestFile, ManifestData manifestData, - ManifestErrorHandler errorHandler) { - super(); - mManifestData = manifestData; - mErrorHandler = errorHandler; - } - - /* (non-Javadoc) - * @see org.xml.sax.helpers.DefaultHandler#setDocumentLocator(org.xml.sax.Locator) - */ - @Override - public void setDocumentLocator(Locator locator) { - mLocator = locator; - super.setDocumentLocator(locator); - } - - /* (non-Javadoc) - * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, - * java.lang.String, org.xml.sax.Attributes) - */ - @Override - public void startElement(String uri, String localName, String name, Attributes attributes) - throws SAXException { - try { - if (mManifestData == null) { - return; - } - - // if we're at a valid level - if (mValidLevel == mCurrentLevel) { - String value; - switch (mValidLevel) { - case LEVEL_TOP: - if (AndroidManifest.NODE_MANIFEST.equals(localName)) { - // lets get the package name. - mManifestData.mPackage = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_PACKAGE, - false /* hasNamespace */); - - // and the versionCode - String tmp = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_VERSIONCODE, true); - if (tmp != null) { - try { - mManifestData.mVersionCode = Integer.valueOf(tmp); - } catch (NumberFormatException e) { - // keep null in the field. - } - } - mValidLevel++; - } - break; - case LEVEL_INSIDE_MANIFEST: - if (AndroidManifest.NODE_APPLICATION.equals(localName)) { - value = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_PROCESS, - true /* hasNamespace */); - if (value != null) { - mManifestData.addProcessName(value); - } - - value = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_DEBUGGABLE, - true /* hasNamespace*/); - if (value != null) { - mManifestData.mDebuggable = Boolean.parseBoolean(value); - } - - mValidLevel++; - } else if (AndroidManifest.NODE_USES_SDK.equals(localName)) { - mManifestData.setMinSdkVersionString(getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION, - true /* hasNamespace */)); - mManifestData.setTargetSdkVersionString(getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_TARGET_SDK_VERSION, - true /* hasNamespace */)); - } else if (AndroidManifest.NODE_INSTRUMENTATION.equals(localName)) { - processInstrumentationNode(attributes); - - } else if (AndroidManifest.NODE_SUPPORTS_SCREENS.equals(localName)) { - processSupportsScreensNode(attributes); - - } else if (AndroidManifest.NODE_USES_CONFIGURATION.equals(localName)) { - processUsesConfiguration(attributes); - - } else if (AndroidManifest.NODE_USES_FEATURE.equals(localName)) { - UsesFeature feature = new UsesFeature(); - - // get the name - value = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_NAME, - true /* hasNamespace */); - if (value != null) { - feature.mName = value; - } - - // read the required attribute - value = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_REQUIRED, - true /*hasNamespace*/); - if (value != null) { - Boolean b = Boolean.valueOf(value); - if (b != null) { - feature.mRequired = b; - } - } - - // read the gl es attribute - value = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_GLESVERSION, - true /*hasNamespace*/); - if (value != null) { - try { - int version = Integer.decode(value); - feature.mGlEsVersion = version; - } catch (NumberFormatException e) { - // ignore - } - - } - - mManifestData.mFeatures.add(feature); - } - break; - case LEVEL_INSIDE_APPLICATION: - if (AndroidManifest.NODE_ACTIVITY.equals(localName) - || AndroidManifest.NODE_ACTIVITY_ALIAS.equals(localName)) { - processActivityNode(attributes); - mValidLevel++; - } else if (AndroidManifest.NODE_SERVICE.equals(localName)) { - processNode(attributes, SdkConstants.CLASS_SERVICE); - mValidLevel++; - } else if (AndroidManifest.NODE_RECEIVER.equals(localName)) { - processNode(attributes, SdkConstants.CLASS_BROADCASTRECEIVER); - mValidLevel++; - } else if (AndroidManifest.NODE_PROVIDER.equals(localName)) { - processNode(attributes, SdkConstants.CLASS_CONTENTPROVIDER); - mValidLevel++; - } else if (AndroidManifest.NODE_USES_LIBRARY.equals(localName)) { - value = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_NAME, - true /* hasNamespace */); - if (value != null) { - UsesLibrary library = new UsesLibrary(); - library.mName = value; - - // read the required attribute - value = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_REQUIRED, - true /*hasNamespace*/); - if (value != null) { - Boolean b = Boolean.valueOf(value); - if (b != null) { - library.mRequired = b; - } - } - - mManifestData.mLibraries.add(library); - } - } - break; - case LEVEL_INSIDE_APP_COMPONENT: - // only process this level if we are in an activity - if (mCurrentActivity != null && - AndroidManifest.NODE_INTENT.equals(localName)) { - mCurrentActivity.resetIntentFilter(); - mValidLevel++; - } - break; - case LEVEL_INSIDE_INTENT_FILTER: - if (mCurrentActivity != null) { - if (AndroidManifest.NODE_ACTION.equals(localName)) { - // get the name attribute - String action = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_NAME, - true /* hasNamespace */); - if (action != null) { - mCurrentActivity.setHasAction(true); - mCurrentActivity.setHasMainAction( - ACTION_MAIN.equals(action)); - } - } else if (AndroidManifest.NODE_CATEGORY.equals(localName)) { - String category = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_NAME, - true /* hasNamespace */); - if (CATEGORY_LAUNCHER.equals(category)) { - mCurrentActivity.setHasLauncherCategory(true); - } - } - - // no need to increase mValidLevel as we don't process anything - // below this level. - } - break; - } - } - - mCurrentLevel++; - } finally { - super.startElement(uri, localName, name, attributes); - } - } - - /* (non-Javadoc) - * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, - * java.lang.String) - */ - @Override - public void endElement(String uri, String localName, String name) throws SAXException { - try { - if (mManifestData == null) { - return; - } - - // decrement the levels. - if (mValidLevel == mCurrentLevel) { - mValidLevel--; - } - mCurrentLevel--; - - // if we're at a valid level - // process the end of the element - if (mValidLevel == mCurrentLevel) { - switch (mValidLevel) { - case LEVEL_INSIDE_APPLICATION: - mCurrentActivity = null; - break; - case LEVEL_INSIDE_APP_COMPONENT: - // if we found both a main action and a launcher category, this is our - // launcher activity! - if (mManifestData.mLauncherActivity == null && - mCurrentActivity != null && - mCurrentActivity.isHomeActivity() && - mCurrentActivity.isExported()) { - mManifestData.mLauncherActivity = mCurrentActivity; - } - break; - default: - break; - } - - } - } finally { - super.endElement(uri, localName, name); - } - } - - /* (non-Javadoc) - * @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException) - */ - @Override - public void error(SAXParseException e) { - if (mErrorHandler != null) { - mErrorHandler.handleError(e, e.getLineNumber()); - } - } - - /* (non-Javadoc) - * @see org.xml.sax.helpers.DefaultHandler#fatalError(org.xml.sax.SAXParseException) - */ - @Override - public void fatalError(SAXParseException e) { - if (mErrorHandler != null) { - mErrorHandler.handleError(e, e.getLineNumber()); - } - } - - /* (non-Javadoc) - * @see org.xml.sax.helpers.DefaultHandler#warning(org.xml.sax.SAXParseException) - */ - @Override - public void warning(SAXParseException e) throws SAXException { - if (mErrorHandler != null) { - mErrorHandler.warning(e); - } - } - - /** - * Processes the activity node. - * @param attributes the attributes for the activity node. - */ - private void processActivityNode(Attributes attributes) { - // lets get the activity name, and add it to the list - String activityName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_NAME, - true /* hasNamespace */); - if (activityName != null) { - activityName = AndroidManifest.combinePackageAndClassName(mManifestData.mPackage, - activityName); - - // get the exported flag. - String exportedStr = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_EXPORTED, true); - boolean exported = exportedStr == null || - exportedStr.toLowerCase(Locale.US).equals("true"); //$NON-NLS-1$ - mCurrentActivity = new Activity(activityName, exported); - mManifestData.mActivities.add(mCurrentActivity); - - if (mErrorHandler != null) { - mErrorHandler.checkClass(mLocator, activityName, SdkConstants.CLASS_ACTIVITY, - true /* testVisibility */); - } - } else { - // no activity found! Aapt will output an error, - // so we don't have to do anything - mCurrentActivity = null; - } - - String processName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_PROCESS, - true /* hasNamespace */); - if (processName != null) { - mManifestData.addProcessName(processName); - } - } - - /** - * Processes the service/receiver/provider nodes. - * @param attributes the attributes for the activity node. - * @param superClassName the fully qualified name of the super class that this - * node is representing - */ - private void processNode(Attributes attributes, String superClassName) { - // lets get the class name, and check it if required. - String serviceName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_NAME, - true /* hasNamespace */); - if (serviceName != null) { - serviceName = AndroidManifest.combinePackageAndClassName(mManifestData.mPackage, - serviceName); - - if (mErrorHandler != null) { - mErrorHandler.checkClass(mLocator, serviceName, superClassName, - false /* testVisibility */); - } - } - - String processName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_PROCESS, - true /* hasNamespace */); - if (processName != null) { - mManifestData.addProcessName(processName); - } - } - - /** - * Processes the instrumentation node. - * @param attributes the attributes for the instrumentation node. - */ - private void processInstrumentationNode(Attributes attributes) { - // lets get the class name, and check it if required. - String instrumentationName = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_NAME, - true /* hasNamespace */); - if (instrumentationName != null) { - String instrClassName = AndroidManifest.combinePackageAndClassName( - mManifestData.mPackage, instrumentationName); - String targetPackage = getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_TARGET_PACKAGE, - true /* hasNamespace */); - mManifestData.mInstrumentations.add( - new Instrumentation(instrClassName, targetPackage)); - if (mErrorHandler != null) { - mErrorHandler.checkClass(mLocator, instrClassName, - SdkConstants.CLASS_INSTRUMENTATION, true /* testVisibility */); - } - } - } - - /** - * Processes the supports-screens node. - * @param attributes the attributes for the supports-screens node. - */ - private void processSupportsScreensNode(Attributes attributes) { - mManifestData.mSupportsScreensFromManifest = new SupportsScreens(); - - mManifestData.mSupportsScreensFromManifest.setResizeable(getAttributeBooleanValue( - attributes, AndroidManifest.ATTRIBUTE_RESIZEABLE, true /*hasNamespace*/)); - - mManifestData.mSupportsScreensFromManifest.setAnyDensity(getAttributeBooleanValue( - attributes, AndroidManifest.ATTRIBUTE_ANYDENSITY, true /*hasNamespace*/)); - - mManifestData.mSupportsScreensFromManifest.setSmallScreens(getAttributeBooleanValue( - attributes, AndroidManifest.ATTRIBUTE_SMALLSCREENS, true /*hasNamespace*/)); - - mManifestData.mSupportsScreensFromManifest.setNormalScreens(getAttributeBooleanValue( - attributes, AndroidManifest.ATTRIBUTE_NORMALSCREENS, true /*hasNamespace*/)); - - mManifestData.mSupportsScreensFromManifest.setLargeScreens(getAttributeBooleanValue( - attributes, AndroidManifest.ATTRIBUTE_LARGESCREENS, true /*hasNamespace*/)); - } - - /** - * Processes the supports-screens node. - * @param attributes the attributes for the supports-screens node. - */ - private void processUsesConfiguration(Attributes attributes) { - mManifestData.mUsesConfiguration = new UsesConfiguration(); - - mManifestData.mUsesConfiguration.mReqFiveWayNav = getAttributeBooleanValue( - attributes, - AndroidManifest.ATTRIBUTE_REQ_5WAYNAV, true /*hasNamespace*/); - mManifestData.mUsesConfiguration.mReqNavigation = Navigation.getEnum( - getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_REQ_NAVIGATION, true /*hasNamespace*/)); - mManifestData.mUsesConfiguration.mReqHardKeyboard = getAttributeBooleanValue( - attributes, - AndroidManifest.ATTRIBUTE_REQ_HARDKEYBOARD, true /*hasNamespace*/); - mManifestData.mUsesConfiguration.mReqKeyboardType = Keyboard.getEnum( - getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_REQ_KEYBOARDTYPE, true /*hasNamespace*/)); - mManifestData.mUsesConfiguration.mReqTouchScreen = TouchScreen.getEnum( - getAttributeValue(attributes, - AndroidManifest.ATTRIBUTE_REQ_TOUCHSCREEN, true /*hasNamespace*/)); - } - - /** - * Searches through the attributes list for a particular one and returns its value. - * @param attributes the attribute list to search through - * @param attributeName the name of the attribute to look for. - * @param hasNamespace Indicates whether the attribute has an android namespace. - * @return a String with the value or null if the attribute was not found. - * @see SdkConstants#NS_RESOURCES - */ - private String getAttributeValue(Attributes attributes, String attributeName, - boolean hasNamespace) { - int count = attributes.getLength(); - for (int i = 0 ; i < count ; i++) { - if (attributeName.equals(attributes.getLocalName(i)) && - ((hasNamespace && - SdkConstants.NS_RESOURCES.equals(attributes.getURI(i))) || - (hasNamespace == false && attributes.getURI(i).length() == 0))) { - return attributes.getValue(i); - } - } - - return null; - } - - /** - * Searches through the attributes list for a particular one and returns its value as a - * Boolean. If the attribute is not present, this will return null. - * @param attributes the attribute list to search through - * @param attributeName the name of the attribute to look for. - * @param hasNamespace Indicates whether the attribute has an android namespace. - * @return a String with the value or null if the attribute was not found. - * @see SdkConstants#NS_RESOURCES - */ - private Boolean getAttributeBooleanValue(Attributes attributes, String attributeName, - boolean hasNamespace) { - int count = attributes.getLength(); - for (int i = 0 ; i < count ; i++) { - if (attributeName.equals(attributes.getLocalName(i)) && - ((hasNamespace && - SdkConstants.NS_RESOURCES.equals(attributes.getURI(i))) || - (hasNamespace == false && attributes.getURI(i).length() == 0))) { - String attr = attributes.getValue(i); - if (attr != null) { - return Boolean.valueOf(attr); - } else { - return null; - } - } - } - - return null; - } - - } - - private final static SAXParserFactory sParserFactory; - - static { - sParserFactory = SAXParserFactory.newInstance(); - sParserFactory.setNamespaceAware(true); - } - - /** - * Parses the Android Manifest, and returns a {@link ManifestData} object containing the - * result of the parsing. - * - * @param manifestFile the {@link IAbstractFile} representing the manifest file. - * @param gatherData indicates whether the parsing will extract data from the manifest. If false - * the method will always return null. - * @param errorHandler an optional errorHandler. - * @return A class containing the manifest info obtained during the parsing, or null on error. - * - * @throws StreamException - * @throws IOException - * @throws SAXException - * @throws ParserConfigurationException - */ - public static ManifestData parse( - IAbstractFile manifestFile, - boolean gatherData, - ManifestErrorHandler errorHandler) - throws SAXException, IOException, StreamException, ParserConfigurationException { - if (manifestFile != null) { - SAXParser parser = sParserFactory.newSAXParser(); - - ManifestData data = null; - if (gatherData) { - data = new ManifestData(); - } - - ManifestHandler manifestHandler = new ManifestHandler(manifestFile, - data, errorHandler); - parser.parse(new InputSource(manifestFile.getContents()), manifestHandler); - - return data; - } - - return null; - } - - /** - * Parses the Android Manifest, and returns an object containing the result of the parsing. - * - * <p/> - * This is the equivalent of calling <pre>parse(manifestFile, true, null)</pre> - * - * @param manifestFile the manifest file to parse. - * - * @throws ParserConfigurationException - * @throws StreamException - * @throws IOException - * @throws SAXException - */ - public static ManifestData parse(IAbstractFile manifestFile) - throws SAXException, IOException, StreamException, ParserConfigurationException { - return parse(manifestFile, true, null); - } - - public static ManifestData parse(IAbstractFolder projectFolder) - throws SAXException, IOException, StreamException, ParserConfigurationException { - IAbstractFile manifestFile = AndroidManifest.getManifest(projectFolder); - if (manifestFile == null) { - throw new FileNotFoundException(); - } - - return parse(manifestFile, true, null); - } - - /** - * Parses the Android Manifest from an {@link InputStream}, and returns a {@link ManifestData} - * object containing the result of the parsing. - * - * @param manifestFileStream the {@link InputStream} representing the manifest file. - * @return A class containing the manifest info obtained during the parsing or null on error. - * - * @throws StreamException - * @throws IOException - * @throws SAXException - * @throws ParserConfigurationException - */ - public static ManifestData parse(InputStream manifestFileStream) - throws SAXException, IOException, StreamException, ParserConfigurationException { - if (manifestFileStream != null) { - SAXParser parser = sParserFactory.newSAXParser(); - - ManifestData data = new ManifestData(); - - ManifestHandler manifestHandler = new ManifestHandler(null, data, null); - parser.parse(new InputSource(manifestFileStream), manifestHandler); - - return data; - } - - return null; - } -} diff --git a/sdk_common/src/com/android/ide/common/xml/ManifestData.java b/sdk_common/src/com/android/ide/common/xml/ManifestData.java deleted file mode 100644 index 9b68d60..0000000 --- a/sdk_common/src/com/android/ide/common/xml/ManifestData.java +++ /dev/null @@ -1,747 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.xml; - -import com.android.resources.Keyboard; -import com.android.resources.Navigation; -import com.android.resources.TouchScreen; - -import java.util.ArrayList; -import java.util.Set; -import java.util.TreeSet; - -/** - * Class containing the manifest info obtained during the parsing. - */ -public final class ManifestData { - - /** - * Value returned by {@link #getMinSdkVersion()} when the value of the minSdkVersion attribute - * in the manifest is a codename and not an integer value. - */ - public final static int MIN_SDK_CODENAME = 0; - - /** - * Value returned by {@link #getGlEsVersion()} when there are no <uses-feature> node with the - * attribute glEsVersion set. - */ - public final static int GL_ES_VERSION_NOT_SET = -1; - - /** Application package */ - String mPackage; - /** Application version Code, null if the attribute is not present. */ - Integer mVersionCode = null; - /** List of all activities */ - final ArrayList<Activity> mActivities = new ArrayList<Activity>(); - /** Launcher activity */ - Activity mLauncherActivity = null; - /** list of process names declared by the manifest */ - Set<String> mProcesses = null; - /** debuggable attribute value. If null, the attribute is not present. */ - Boolean mDebuggable = null; - /** API level requirement. if null the attribute was not present. */ - private String mMinSdkVersionString = null; - /** API level requirement. Default is 1 even if missing. If value is a codename, then it'll be - * 0 instead. */ - private int mMinSdkVersion = 1; - private int mTargetSdkVersion = 0; - /** List of all instrumentations declared by the manifest */ - final ArrayList<Instrumentation> mInstrumentations = - new ArrayList<Instrumentation>(); - /** List of all libraries in use declared by the manifest */ - final ArrayList<UsesLibrary> mLibraries = new ArrayList<UsesLibrary>(); - /** List of all feature in use declared by the manifest */ - final ArrayList<UsesFeature> mFeatures = new ArrayList<UsesFeature>(); - - SupportsScreens mSupportsScreensFromManifest; - SupportsScreens mSupportsScreensValues; - UsesConfiguration mUsesConfiguration; - - /** - * Instrumentation info obtained from manifest - */ - public final static class Instrumentation { - private final String mName; - private final String mTargetPackage; - - Instrumentation(String name, String targetPackage) { - mName = name; - mTargetPackage = targetPackage; - } - - /** - * Returns the fully qualified instrumentation class name - */ - public String getName() { - return mName; - } - - /** - * Returns the Android app package that is the target of this instrumentation - */ - public String getTargetPackage() { - return mTargetPackage; - } - } - - /** - * Activity info obtained from the manifest. - */ - public final static class Activity { - private final String mName; - private final boolean mIsExported; - private boolean mHasAction = false; - private boolean mHasMainAction = false; - private boolean mHasLauncherCategory = false; - - public Activity(String name, boolean exported) { - mName = name; - mIsExported = exported; - } - - public String getName() { - return mName; - } - - public boolean isExported() { - return mIsExported; - } - - public boolean hasAction() { - return mHasAction; - } - - public boolean isHomeActivity() { - return mHasMainAction && mHasLauncherCategory; - } - - void setHasAction(boolean hasAction) { - mHasAction = hasAction; - } - - /** If the activity doesn't yet have a filter set for the launcher, this resets both - * flags. This is to handle multiple intent-filters where one could have the valid - * action, and another one of the valid category. - */ - void resetIntentFilter() { - if (isHomeActivity() == false) { - mHasMainAction = mHasLauncherCategory = false; - } - } - - void setHasMainAction(boolean hasMainAction) { - mHasMainAction = hasMainAction; - } - - void setHasLauncherCategory(boolean hasLauncherCategory) { - mHasLauncherCategory = hasLauncherCategory; - } - } - - /** - * Class representing the <code>supports-screens</code> node in the manifest. - * By default, all the getters will return null if there was no value defined in the manifest. - * - * To get an instance with all the actual values, use {@link #resolveSupportsScreensValues(int)} - */ - public final static class SupportsScreens { - private Boolean mResizeable; - private Boolean mAnyDensity; - private Boolean mSmallScreens; - private Boolean mNormalScreens; - private Boolean mLargeScreens; - - public SupportsScreens() { - } - - /** - * Instantiate an instance from a string. The string must have been created with - * {@link #getEncodedValues()}. - * @param value the string. - */ - public SupportsScreens(String value) { - String[] values = value.split("\\|"); - - mAnyDensity = Boolean.valueOf(values[0]); - mResizeable = Boolean.valueOf(values[1]); - mSmallScreens = Boolean.valueOf(values[2]); - mNormalScreens = Boolean.valueOf(values[3]); - mLargeScreens = Boolean.valueOf(values[4]); - } - - /** - * Returns an instance of {@link SupportsScreens} initialized with the default values - * based on the given targetSdkVersion. - * @param targetSdkVersion - */ - public static SupportsScreens getDefaultValues(int targetSdkVersion) { - SupportsScreens result = new SupportsScreens(); - - result.mNormalScreens = Boolean.TRUE; - // Screen size and density became available in Android 1.5/API3, so before that - // non normal screens were not supported by default. After they are considered - // supported. - result.mResizeable = result.mAnyDensity = result.mSmallScreens = result.mLargeScreens = - targetSdkVersion <= 3 ? Boolean.FALSE : Boolean.TRUE; - - return result; - } - - /** - * Returns a version of the receiver for which all values have been set, even if they - * were not present in the manifest. - * @param targetSdkVersion the target api level of the app, since this has an effect - * on default values. - */ - public SupportsScreens resolveSupportsScreensValues(int targetSdkVersion) { - SupportsScreens result = getDefaultValues(targetSdkVersion); - - // Override the default with the existing values: - if (mResizeable != null) result.mResizeable = mResizeable; - if (mAnyDensity != null) result.mAnyDensity = mAnyDensity; - if (mSmallScreens != null) result.mSmallScreens = mSmallScreens; - if (mNormalScreens != null) result.mNormalScreens = mNormalScreens; - if (mLargeScreens != null) result.mLargeScreens = mLargeScreens; - - return result; - } - - /** - * returns the value of the <code>resizeable</code> attribute or null if not present. - */ - public Boolean getResizeable() { - return mResizeable; - } - - void setResizeable(Boolean resizeable) { - mResizeable = getConstantBoolean(resizeable); - } - - /** - * returns the value of the <code>anyDensity</code> attribute or null if not present. - */ - public Boolean getAnyDensity() { - return mAnyDensity; - } - - void setAnyDensity(Boolean anyDensity) { - mAnyDensity = getConstantBoolean(anyDensity); - } - - /** - * returns the value of the <code>smallScreens</code> attribute or null if not present. - */ - public Boolean getSmallScreens() { - return mSmallScreens; - } - - void setSmallScreens(Boolean smallScreens) { - mSmallScreens = getConstantBoolean(smallScreens); - } - - /** - * returns the value of the <code>normalScreens</code> attribute or null if not present. - */ - public Boolean getNormalScreens() { - return mNormalScreens; - } - - void setNormalScreens(Boolean normalScreens) { - mNormalScreens = getConstantBoolean(normalScreens); - } - - /** - * returns the value of the <code>largeScreens</code> attribute or null if not present. - */ - public Boolean getLargeScreens() { - return mLargeScreens; - } - - void setLargeScreens(Boolean largeScreens) { - mLargeScreens = getConstantBoolean(largeScreens); - } - - /** - * Returns either {@link Boolean#TRUE} or {@link Boolean#FALSE} based on the value of - * the given Boolean object. - */ - private Boolean getConstantBoolean(Boolean v) { - if (v != null) { - if (v.equals(Boolean.TRUE)) { - return Boolean.TRUE; - } else { - return Boolean.FALSE; - } - } - - return null; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof SupportsScreens) { - SupportsScreens support = (SupportsScreens) obj; - // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE - // (or null), we can simply check they are identical and not bother with - // calling equals (which would require to check != null. - // see #getConstanntBoolean(Boolean) - return mResizeable == support.mResizeable && - mAnyDensity == support.mAnyDensity && - mSmallScreens == support.mSmallScreens && - mNormalScreens == support.mNormalScreens && - mLargeScreens == support.mLargeScreens; - } - - return false; - } - - /* Override hashCode, mostly to make Eclipse happy and not warn about it. - * And if you ever put this in a Map or Set, it will avoid surprises. */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mAnyDensity == null) ? 0 : mAnyDensity.hashCode()); - result = prime * result + ((mLargeScreens == null) ? 0 : mLargeScreens.hashCode()); - result = prime * result + ((mNormalScreens == null) ? 0 : mNormalScreens.hashCode()); - result = prime * result + ((mResizeable == null) ? 0 : mResizeable.hashCode()); - result = prime * result + ((mSmallScreens == null) ? 0 : mSmallScreens.hashCode()); - return result; - } - - /** - * Returns true if the two instances support the same screen sizes. - * This is similar to {@link #equals(Object)} except that it ignores the values of - * {@link #getAnyDensity()} and {@link #getResizeable()}. - * @param support the other instance to compare to. - * @return true if the two instances support the same screen sizes. - */ - public boolean hasSameScreenSupportAs(SupportsScreens support) { - // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE - // (or null), we can simply check they are identical and not bother with - // calling equals (which would require to check != null. - // see #getConstanntBoolean(Boolean) - - // This only checks that matter here are the screen sizes. resizeable and anyDensity - // are not checked. - return mSmallScreens == support.mSmallScreens && - mNormalScreens == support.mNormalScreens && - mLargeScreens == support.mLargeScreens; - } - - /** - * Returns true if the two instances have strictly different screen size support. - * This means that there is no screen size that they both support. - * @param support the other instance to compare to. - * @return true if they are stricly different. - */ - public boolean hasStrictlyDifferentScreenSupportAs(SupportsScreens support) { - // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE - // (or null), we can simply check they are identical and not bother with - // calling equals (which would require to check != null. - // see #getConstanntBoolean(Boolean) - - // This only checks that matter here are the screen sizes. resizeable and anyDensity - // are not checked. - return (mSmallScreens != Boolean.TRUE || support.mSmallScreens != Boolean.TRUE) && - (mNormalScreens != Boolean.TRUE || support.mNormalScreens != Boolean.TRUE) && - (mLargeScreens != Boolean.TRUE || support.mLargeScreens != Boolean.TRUE); - } - - /** - * Comparison of 2 Supports-screens. This only uses screen sizes (ignores resizeable and - * anyDensity), and considers that - * {@link #hasStrictlyDifferentScreenSupportAs(SupportsScreens)} returns true and - * {@link #overlapWith(SupportsScreens)} returns false. - * @throws IllegalArgumentException if the two instanced are not strictly different or - * overlap each other - * @see #hasStrictlyDifferentScreenSupportAs(SupportsScreens) - * @see #overlapWith(SupportsScreens) - */ - public int compareScreenSizesWith(SupportsScreens o) { - if (hasStrictlyDifferentScreenSupportAs(o) == false) { - throw new IllegalArgumentException("The two instances are not strictly different."); - } - if (overlapWith(o)) { - throw new IllegalArgumentException("The two instances overlap each other."); - } - - int comp = mLargeScreens.compareTo(o.mLargeScreens); - if (comp != 0) return comp; - - comp = mNormalScreens.compareTo(o.mNormalScreens); - if (comp != 0) return comp; - - comp = mSmallScreens.compareTo(o.mSmallScreens); - if (comp != 0) return comp; - - return 0; - } - - /** - * Returns a string encoding of the content of the instance. This string can be used to - * instantiate a {@link SupportsScreens} object through - * {@link #SupportsScreens(String)}. - */ - public String getEncodedValues() { - return String.format("%1$s|%2$s|%3$s|%4$s|%5$s", - mAnyDensity, mResizeable, mSmallScreens, mNormalScreens, mLargeScreens); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - - boolean alreadyOutputSomething = false; - - if (Boolean.TRUE.equals(mSmallScreens)) { - alreadyOutputSomething = true; - sb.append("small"); - } - - if (Boolean.TRUE.equals(mNormalScreens)) { - if (alreadyOutputSomething) { - sb.append(", "); - } - alreadyOutputSomething = true; - sb.append("normal"); - } - - if (Boolean.TRUE.equals(mLargeScreens)) { - if (alreadyOutputSomething) { - sb.append(", "); - } - alreadyOutputSomething = true; - sb.append("large"); - } - - if (alreadyOutputSomething == false) { - sb.append("<none>"); - } - - return sb.toString(); - } - - /** - * Returns true if the two instance overlap with each other. - * This can happen if one instances supports a size, when the other instance doesn't while - * supporting a size above and a size below. - * @param otherSS the other supports-screens to compare to. - */ - public boolean overlapWith(SupportsScreens otherSS) { - if (mSmallScreens == null || mNormalScreens == null || mLargeScreens == null || - otherSS.mSmallScreens == null || otherSS.mNormalScreens == null || - otherSS.mLargeScreens == null) { - throw new IllegalArgumentException("Some screen sizes Boolean are not initialized"); - } - - if (mSmallScreens == Boolean.TRUE && mNormalScreens == Boolean.FALSE && - mLargeScreens == Boolean.TRUE) { - return otherSS.mNormalScreens == Boolean.TRUE; - } - - if (otherSS.mSmallScreens == Boolean.TRUE && otherSS.mNormalScreens == Boolean.FALSE && - otherSS.mLargeScreens == Boolean.TRUE) { - return mNormalScreens == Boolean.TRUE; - } - - return false; - } - } - - /** - * Class representing a <code>uses-library</code> node in the manifest. - */ - public final static class UsesLibrary { - String mName; - Boolean mRequired = Boolean.TRUE; // default is true even if missing - - public String getName() { - return mName; - } - - public Boolean getRequired() { - return mRequired; - } - } - - /** - * Class representing a <code>uses-feature</code> node in the manifest. - */ - public final static class UsesFeature { - String mName; - int mGlEsVersion = 0; - Boolean mRequired = Boolean.TRUE; // default is true even if missing - - public String getName() { - return mName; - } - - /** - * Returns the value of the glEsVersion attribute, or 0 if the attribute was not present. - */ - public int getGlEsVersion() { - return mGlEsVersion; - } - - public Boolean getRequired() { - return mRequired; - } - } - - /** - * Class representing the <code>uses-configuration</code> node in the manifest. - */ - public final static class UsesConfiguration { - Boolean mReqFiveWayNav; - Boolean mReqHardKeyboard; - Keyboard mReqKeyboardType; - TouchScreen mReqTouchScreen; - Navigation mReqNavigation; - - /** - * returns the value of the <code>reqFiveWayNav</code> attribute or null if not present. - */ - public Boolean getReqFiveWayNav() { - return mReqFiveWayNav; - } - - /** - * returns the value of the <code>reqNavigation</code> attribute or null if not present. - */ - public Navigation getReqNavigation() { - return mReqNavigation; - } - - /** - * returns the value of the <code>reqHardKeyboard</code> attribute or null if not present. - */ - public Boolean getReqHardKeyboard() { - return mReqHardKeyboard; - } - - /** - * returns the value of the <code>reqKeyboardType</code> attribute or null if not present. - */ - public Keyboard getReqKeyboardType() { - return mReqKeyboardType; - } - - /** - * returns the value of the <code>reqTouchScreen</code> attribute or null if not present. - */ - public TouchScreen getReqTouchScreen() { - return mReqTouchScreen; - } - } - - /** - * Returns the package defined in the manifest, if found. - * @return The package name or null if not found. - */ - public String getPackage() { - return mPackage; - } - - /** - * Returns the versionCode value defined in the manifest, if found, null otherwise. - * @return the versionCode or null if not found. - */ - public Integer getVersionCode() { - return mVersionCode; - } - - /** - * Returns the list of activities found in the manifest. - * @return An array of fully qualified class names, or empty if no activity were found. - */ - public Activity[] getActivities() { - return mActivities.toArray(new Activity[mActivities.size()]); - } - - /** - * Returns the name of one activity found in the manifest, that is configured to show - * up in the HOME screen. - * @return the fully qualified name of a HOME activity or null if none were found. - */ - public Activity getLauncherActivity() { - return mLauncherActivity; - } - - /** - * Returns the list of process names declared by the manifest. - */ - public String[] getProcesses() { - if (mProcesses != null) { - return mProcesses.toArray(new String[mProcesses.size()]); - } - - return new String[0]; - } - - /** - * Returns the <code>debuggable</code> attribute value or null if it is not set. - */ - public Boolean getDebuggable() { - return mDebuggable; - } - - /** - * Returns the <code>minSdkVersion</code> attribute, or null if it's not set. - */ - public String getMinSdkVersionString() { - return mMinSdkVersionString; - } - - /** - * Sets the value of the <code>minSdkVersion</code> attribute. - * @param minSdkVersion the string value of the attribute in the manifest. - */ - public void setMinSdkVersionString(String minSdkVersion) { - mMinSdkVersionString = minSdkVersion; - if (mMinSdkVersionString != null) { - try { - mMinSdkVersion = Integer.parseInt(mMinSdkVersionString); - } catch (NumberFormatException e) { - mMinSdkVersion = MIN_SDK_CODENAME; - } - } - } - - /** - * Returns the <code>minSdkVersion</code> attribute, or 0 if it's not set or is a codename. - * @see #getMinSdkVersionString() - */ - public int getMinSdkVersion() { - return mMinSdkVersion; - } - - - /** - * Sets the value of the <code>minSdkVersion</code> attribute. - * @param targetSdkVersion the string value of the attribute in the manifest. - */ - public void setTargetSdkVersionString(String targetSdkVersion) { - if (targetSdkVersion != null) { - try { - mTargetSdkVersion = Integer.parseInt(targetSdkVersion); - } catch (NumberFormatException e) { - // keep the value at 0. - } - } - } - - /** - * Returns the <code>targetSdkVersion</code> attribute, or the same value as - * {@link #getMinSdkVersion()} if it was not set in the manifest. - */ - public int getTargetSdkVersion() { - if (mTargetSdkVersion == 0) { - return getMinSdkVersion(); - } - - return mTargetSdkVersion; - } - - /** - * Returns the list of instrumentations found in the manifest. - * @return An array of {@link Instrumentation}, or empty if no instrumentations were - * found. - */ - public Instrumentation[] getInstrumentations() { - return mInstrumentations.toArray(new Instrumentation[mInstrumentations.size()]); - } - - /** - * Returns the list of libraries in use found in the manifest. - * @return An array of {@link UsesLibrary} objects, or empty if no libraries were found. - */ - public UsesLibrary[] getUsesLibraries() { - return mLibraries.toArray(new UsesLibrary[mLibraries.size()]); - } - - /** - * Returns the list of features in use found in the manifest. - * @return An array of {@link UsesFeature} objects, or empty if no libraries were found. - */ - public UsesFeature[] getUsesFeatures() { - return mFeatures.toArray(new UsesFeature[mFeatures.size()]); - } - - /** - * Returns the glEsVersion from a <uses-feature> or {@link #GL_ES_VERSION_NOT_SET} if not set. - */ - public int getGlEsVersion() { - for (UsesFeature feature : mFeatures) { - if (feature.mGlEsVersion > 0) { - return feature.mGlEsVersion; - } - } - return GL_ES_VERSION_NOT_SET; - } - - /** - * Returns the {@link SupportsScreens} object representing the <code>supports-screens</code> - * node, or null if the node doesn't exist at all. - * Some values in the {@link SupportsScreens} instance maybe null, indicating that they - * were not present in the manifest. To get an instance that contains the values, as seen - * by the Android platform when the app is running, use {@link #getSupportsScreensValues()}. - */ - public SupportsScreens getSupportsScreensFromManifest() { - return mSupportsScreensFromManifest; - } - - /** - * Returns an always non-null instance of {@link SupportsScreens} that's been initialized with - * the default values, and the values from the manifest. - * The default values depends on the manifest values for minSdkVersion and targetSdkVersion. - */ - public synchronized SupportsScreens getSupportsScreensValues() { - if (mSupportsScreensValues == null) { - if (mSupportsScreensFromManifest == null) { - mSupportsScreensValues = SupportsScreens.getDefaultValues(getTargetSdkVersion()); - } else { - // get a SupportsScreen that replace the missing values with default values. - mSupportsScreensValues = mSupportsScreensFromManifest.resolveSupportsScreensValues( - getTargetSdkVersion()); - } - } - - return mSupportsScreensValues; - } - - /** - * Returns the {@link UsesConfiguration} object representing the <code>uses-configuration</code> - * node, or null if the node doesn't exist at all. - */ - public UsesConfiguration getUsesConfiguration() { - return mUsesConfiguration; - } - - void addProcessName(String processName) { - if (mProcesses == null) { - mProcesses = new TreeSet<String>(); - } - - if (processName.startsWith(":")) { - mProcesses.add(mPackage + processName); - } else { - mProcesses.add(processName); - } - } - -} diff --git a/sdk_common/tests/.classpath b/sdk_common/tests/.classpath deleted file mode 100644 index 1e45d8a..0000000 --- a/sdk_common/tests/.classpath +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> - <classpathentry combineaccessrules="false" kind="src" path="/sdk_common"/> - <classpathentry combineaccessrules="false" kind="src" path="/common"/> - <classpathentry combineaccessrules="false" kind="src" path="/layoutlib_api"/> - <classpathentry kind="output" path="bin"/> -</classpath> diff --git a/sdk_common/tests/.project b/sdk_common/tests/.project deleted file mode 100644 index 86d2f15..0000000 --- a/sdk_common/tests/.project +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>sdk_common-tests</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> diff --git a/sdk_common/tests/.settings/org.moreunit.prefs b/sdk_common/tests/.settings/org.moreunit.prefs deleted file mode 100644 index 902b677..0000000 --- a/sdk_common/tests/.settings/org.moreunit.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -org.moreunit.prefixes= -org.moreunit.unitsourcefolder=sdk_common-tests\:src\:sdk_common\:src -org.moreunit.useprojectsettings=true diff --git a/sdk_common/tests/Android.mk b/sdk_common/tests/Android.mk deleted file mode 100644 index e7f8c8d..0000000 --- a/sdk_common/tests/Android.mk +++ /dev/null @@ -1,27 +0,0 @@ -# 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. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -# Only compile source java files in this lib. -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_MODULE := sdk_common-tests -LOCAL_MODULE_TAGS := optional - -LOCAL_JAVA_LIBRARIES := common sdk_common junit - -include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/sdk_common/tests/src/com/android/ide/common/resources/ResourceRepositoryTest.java b/sdk_common/tests/src/com/android/ide/common/resources/ResourceRepositoryTest.java deleted file mode 100644 index a8b8b1e..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/ResourceRepositoryTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.ide.common.resources; - -import static com.android.resources.ResourceType.ATTR; -import static com.android.resources.ResourceType.DIMEN; -import static com.android.resources.ResourceType.LAYOUT; -import junit.framework.TestCase; - -@SuppressWarnings("javadoc") -public class ResourceRepositoryTest extends TestCase { - public void testParseResource() { - assertNull(ResourceRepository.parseResource("")); - assertNull(ResourceRepository.parseResource("not_a_resource")); - - assertEquals(LAYOUT, ResourceRepository.parseResource("@layout/foo").getFirst()); - assertEquals(DIMEN, ResourceRepository.parseResource("@dimen/foo").getFirst()); - assertEquals(DIMEN, ResourceRepository.parseResource("@android:dimen/foo").getFirst()); - assertEquals("foo", ResourceRepository.parseResource("@layout/foo").getSecond()); - assertEquals("foo", ResourceRepository.parseResource("@dimen/foo").getSecond()); - assertEquals("foo", ResourceRepository.parseResource("@android:dimen/foo").getSecond()); - - assertEquals(ATTR, ResourceRepository.parseResource("?attr/foo").getFirst()); - assertEquals("foo", ResourceRepository.parseResource("?attr/foo").getSecond()); - - assertEquals(ATTR, ResourceRepository.parseResource("?foo").getFirst()); - assertEquals("foo", ResourceRepository.parseResource("?foo").getSecond()); - - assertEquals(ATTR, ResourceRepository.parseResource("?android:foo").getFirst()); - assertEquals("foo", ResourceRepository.parseResource("?android:foo").getSecond()); - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/ValueResourceParserTest.java b/sdk_common/tests/src/com/android/ide/common/resources/ValueResourceParserTest.java deleted file mode 100644 index aed6060..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/ValueResourceParserTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2013 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.ide.common.resources; - -import static com.android.ide.common.resources.ValueResourceParser.escapeResourceString; -import static com.android.ide.common.resources.ValueResourceParser.isEscaped; -import static com.android.ide.common.resources.ValueResourceParser.unescapeResourceString; - -import junit.framework.TestCase; - -public class ValueResourceParserTest extends TestCase { - - public void testEscapeStringShouldEscapeXmlSpecialCharacters() throws Exception { - assertEquals("<", escapeResourceString("<")); - assertEquals("&", escapeResourceString("&")); - } - - public void testEscapeStringShouldEscapeQuotes() throws Exception { - assertEquals("\\'", escapeResourceString("'")); - assertEquals("\\\"", escapeResourceString("\"")); - assertEquals("\" ' \"", escapeResourceString(" ' ")); - } - - public void testEscapeStringShouldPreserveWhitespace() throws Exception { - assertEquals("\"at end \"", escapeResourceString("at end ")); - assertEquals("\" at begin\"", escapeResourceString(" at begin")); - } - - public void testEscapeStringShouldEscapeAtSignAndQuestionMarkOnlyAtBeginning() - throws Exception { - assertEquals("\\@text", escapeResourceString("@text")); - assertEquals("a@text", escapeResourceString("a@text")); - assertEquals("\\?text", escapeResourceString("?text")); - assertEquals("a?text", escapeResourceString("a?text")); - assertEquals("\" ?text\"", escapeResourceString(" ?text")); - } - - public void testEscapeStringShouldEscapeJavaEscapeSequences() throws Exception { - assertEquals("\\n", escapeResourceString("\n")); - assertEquals("\\t", escapeResourceString("\t")); - assertEquals("\\\\", escapeResourceString("\\")); - } - - public void testTrim() throws Exception { - assertEquals("", unescapeResourceString("", false, true)); - assertEquals("", unescapeResourceString(" \n ", false, true)); - assertEquals("test", unescapeResourceString(" test ", false, true)); - assertEquals(" test ", unescapeResourceString("\" test \"", false, true)); - assertEquals("test", unescapeResourceString("\n\t test \t\n ", false, true)); - - assertEquals("test\n", unescapeResourceString(" test\\n ", false, true)); - assertEquals(" test\n ", unescapeResourceString("\" test\\n \"", false, true)); - assertEquals("te\\st", unescapeResourceString("\n\t te\\\\st \t\n ", false, true)); - assertEquals("te\\st", unescapeResourceString(" te\\\\st ", false, true)); - assertEquals("test", unescapeResourceString("\"\"\"test\"\" ", false, true)); - assertEquals("\"test\"", unescapeResourceString("\"\"\\\"test\\\"\" ", false, true)); - assertEquals("test ", unescapeResourceString("test\\ ", false, true)); - assertEquals("\\\\\\", unescapeResourceString("\\\\\\\\\\\\ ", false, true)); - assertEquals("\\\\\\ ", unescapeResourceString("\\\\\\\\\\\\\\ ", false, true)); - } - - public void testNoTrim() throws Exception { - assertEquals("", unescapeResourceString("", false, false)); - assertEquals(" \n ", unescapeResourceString(" \n ", false, false)); - assertEquals(" test ", unescapeResourceString(" test ", false, false)); - assertEquals("\" test \"", unescapeResourceString("\" test \"", false, false)); - assertEquals("\n\t test \t\n ", unescapeResourceString("\n\t test \t\n ", false, false)); - - assertEquals(" test\n ", unescapeResourceString(" test\\n ", false, false)); - assertEquals("\" test\n \"", unescapeResourceString("\" test\\n \"", false, false)); - assertEquals("\n\t te\\st \t\n ", unescapeResourceString("\n\t te\\\\st \t\n ", false, false)); - assertEquals(" te\\st ", unescapeResourceString(" te\\\\st ", false, false)); - assertEquals("\"\"\"test\"\" ", unescapeResourceString("\"\"\"test\"\" ", false, false)); - assertEquals("\"\"\"test\"\" ", unescapeResourceString("\"\"\\\"test\\\"\" ", false, false)); - assertEquals("test ", unescapeResourceString("test\\ ", false, false)); - assertEquals("\\\\\\ ", unescapeResourceString("\\\\\\\\\\\\ ", false, false)); - assertEquals("\\\\\\ ", unescapeResourceString("\\\\\\\\\\\\\\ ", false, false)); - } - - public void testUnescapeStringShouldUnescapeXmlSpecialCharacters() throws Exception { - assertEquals("<", unescapeResourceString("<", false, true)); - assertEquals("<", unescapeResourceString("<", true, true)); - assertEquals("<", unescapeResourceString(" < ", true, true)); - assertEquals("&", unescapeResourceString("&", false, true)); - assertEquals("&", unescapeResourceString("&", true, true)); - assertEquals("&", unescapeResourceString(" & ", true, true)); - assertEquals("!<", unescapeResourceString("!<", true, true)); - } - - public void testUnescapeStringShouldUnescapeQuotes() throws Exception { - assertEquals("'", unescapeResourceString("\\'", false, true)); - assertEquals("\"", unescapeResourceString("\\\"", false, true)); - assertEquals(" ' ", unescapeResourceString("\" ' \"", false, true)); - } - - public void testUnescapeStringShouldPreserveWhitespace() throws Exception { - assertEquals("at end ", unescapeResourceString("\"at end \"", false, true)); - assertEquals(" at begin", unescapeResourceString("\" at begin\"", false, true)); - } - - public void testUnescapeStringShouldUnescapeAtSignAndQuestionMarkOnlyAtBeginning() - throws Exception { - assertEquals("@text", unescapeResourceString("\\@text", false, true)); - assertEquals("a@text", unescapeResourceString("a@text", false, true)); - assertEquals("?text", unescapeResourceString("\\?text", false, true)); - assertEquals("a?text", unescapeResourceString("a?text", false, true)); - assertEquals(" ?text", unescapeResourceString("\" ?text\"", false, true)); - } - - public void testUnescapeStringShouldUnescapeJavaUnescapeSequences() throws Exception { - assertEquals("\n", unescapeResourceString("\\n", false, true)); - assertEquals("\t", unescapeResourceString("\\t", false, true)); - assertEquals("\\", unescapeResourceString("\\\\", false, true)); - } - - public void testIsEscaped() throws Exception { - assertFalse(isEscaped("", 0)); - assertFalse(isEscaped(" ", 0)); - assertFalse(isEscaped(" ", 1)); - assertFalse(isEscaped("x\\y ", 0)); - assertFalse(isEscaped("x\\y ", 1)); - assertTrue(isEscaped("x\\y ", 2)); - assertFalse(isEscaped("x\\y ", 3)); - assertFalse(isEscaped("x\\\\y ", 0)); - assertFalse(isEscaped("x\\\\y ", 1)); - assertTrue(isEscaped("x\\\\y ", 2)); - assertFalse(isEscaped("x\\\\y ", 3)); - assertFalse(isEscaped("\\\\\\\\y ", 0)); - assertTrue(isEscaped( "\\\\\\\\y ", 1)); - assertFalse(isEscaped("\\\\\\\\y ", 2)); - assertTrue(isEscaped( "\\\\\\\\y ", 3)); - assertFalse(isEscaped("\\\\\\\\y ", 4)); - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/CountryCodeQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/CountryCodeQualifierTest.java deleted file mode 100644 index eba8b8d..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/CountryCodeQualifierTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.resources.configuration; - -import junit.framework.TestCase; - -public class CountryCodeQualifierTest extends TestCase { - - private CountryCodeQualifier mccq; - private FolderConfiguration config; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mccq = new CountryCodeQualifier(); - config = new FolderConfiguration(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - mccq = null; - config = null; - } - - public void testCheckAndSet() { - assertEquals(true, mccq.checkAndSet("mcc123", config));//$NON-NLS-1$ - assertTrue(config.getCountryCodeQualifier() != null); - assertEquals(123, config.getCountryCodeQualifier().getCode()); - assertEquals("mcc123", config.getCountryCodeQualifier().toString()); //$NON-NLS-1$ - } - - public void testFailures() { - assertEquals(false, mccq.checkAndSet("", config));//$NON-NLS-1$ - assertEquals(false, mccq.checkAndSet("mcc", config));//$NON-NLS-1$ - assertEquals(false, mccq.checkAndSet("MCC123", config));//$NON-NLS-1$ - assertEquals(false, mccq.checkAndSet("123", config));//$NON-NLS-1$ - assertEquals(false, mccq.checkAndSet("mccsdf", config));//$NON-NLS-1$ - } - -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/DockModeQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/DockModeQualifierTest.java deleted file mode 100644 index 1653805..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/DockModeQualifierTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.resources.configuration; - -import com.android.resources.UiMode; - -import junit.framework.TestCase; - -public class DockModeQualifierTest extends TestCase { - - private UiModeQualifier mCarQualifier; - private UiModeQualifier mDeskQualifier; - private UiModeQualifier mTVQualifier; - private UiModeQualifier mNoneQualifier; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mCarQualifier = new UiModeQualifier(UiMode.CAR); - mDeskQualifier = new UiModeQualifier(UiMode.DESK); - mTVQualifier = new UiModeQualifier(UiMode.TELEVISION); - mNoneQualifier = new UiModeQualifier(UiMode.NORMAL); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - mCarQualifier = null; - mDeskQualifier = null; - mTVQualifier = null; - mNoneQualifier = null; - } - - public void testIsBetterMatchThan() { - assertTrue(mNoneQualifier.isBetterMatchThan(mCarQualifier, mDeskQualifier)); - assertTrue(mNoneQualifier.isBetterMatchThan(mCarQualifier, mDeskQualifier)); - assertFalse(mNoneQualifier.isBetterMatchThan(mDeskQualifier, mDeskQualifier)); - assertTrue(mNoneQualifier.isBetterMatchThan(mDeskQualifier, mCarQualifier)); - assertFalse(mNoneQualifier.isBetterMatchThan(mCarQualifier, mCarQualifier)); - - assertTrue(mDeskQualifier.isBetterMatchThan(mCarQualifier, mDeskQualifier)); - assertFalse(mDeskQualifier.isBetterMatchThan(mCarQualifier, mCarQualifier)); - - assertTrue(mCarQualifier.isBetterMatchThan(mDeskQualifier, mCarQualifier)); - assertFalse(mCarQualifier.isBetterMatchThan(mDeskQualifier, mDeskQualifier)); - - assertTrue(mTVQualifier.isBetterMatchThan(mCarQualifier, mTVQualifier)); - assertFalse(mTVQualifier.isBetterMatchThan(mDeskQualifier, mDeskQualifier)); - - } - - public void testIsMatchFor() { - assertTrue(mNoneQualifier.isMatchFor(mCarQualifier)); - assertTrue(mNoneQualifier.isMatchFor(mDeskQualifier)); - assertTrue(mNoneQualifier.isMatchFor(mTVQualifier)); - assertTrue(mCarQualifier.isMatchFor(mCarQualifier)); - assertTrue(mDeskQualifier.isMatchFor(mDeskQualifier)); - assertTrue(mTVQualifier.isMatchFor(mTVQualifier)); - - assertFalse(mCarQualifier.isMatchFor(mNoneQualifier)); - assertFalse(mCarQualifier.isMatchFor(mDeskQualifier)); - - assertFalse(mDeskQualifier.isMatchFor(mCarQualifier)); - assertFalse(mDeskQualifier.isMatchFor(mNoneQualifier)); - - assertFalse(mTVQualifier.isMatchFor(mCarQualifier)); - assertFalse(mTVQualifier.isMatchFor(mNoneQualifier)); - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/FolderConfigurationTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/FolderConfigurationTest.java deleted file mode 100644 index 5f29791..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/FolderConfigurationTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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.ide.common.resources.configuration; - -import java.util.ArrayList; -import java.util.List; - -import junit.framework.TestCase; - -public class FolderConfigurationTest extends TestCase { - - /* - * Test createDefault creates all the qualifiers. - */ - public void testCreateDefault() { - FolderConfiguration defaultConfig = new FolderConfiguration(); - defaultConfig.createDefault(); - - // this is always valid and up to date. - final int count = FolderConfiguration.getQualifierCount(); - - // make sure all the qualifiers were created. - for (int i = 0 ; i < count ; i++) { - assertNotNull(defaultConfig.getQualifier(i)); - } - } - - public void testSimpleResMatch() { - runConfigMatchTest( - "en-rGB-port-hdpi-notouch-12key", - 3, - "", - "en", - "fr-rCA", - "en-port", - "en-notouch-12key", - "port-ldpi", - "port-notouch-12key"); - } - - public void testVersionResMatch() { - runConfigMatchTest( - "en-rUS-w600dp-h1024dp-large-port-mdpi-finger-nokeys-v12", - 2, - "", - "large", - "w540dp"); - } - - public void testAddQualifier() { - FolderConfiguration defaultConfig = new FolderConfiguration(); - defaultConfig.createDefault(); - - final int count = FolderConfiguration.getQualifierCount(); - for (int i = 0 ; i < count ; i++) { - FolderConfiguration empty = new FolderConfiguration(); - - ResourceQualifier q = defaultConfig.getQualifier(i); - - empty.addQualifier(q); - - // check it was added - assertNotNull( - "addQualifier failed for " + q.getClass().getName(), empty.getQualifier(i)); - } - } - - - // --- helper methods - - private final static class MockConfigurable implements Configurable { - - private final FolderConfiguration mConfig; - - MockConfigurable(String config) { - mConfig = FolderConfiguration.getConfig(getFolderSegments(config)); - } - - @Override - public FolderConfiguration getConfiguration() { - return mConfig; - } - - @Override - public String toString() { - return mConfig.toString(); - } - } - - private void runConfigMatchTest(String refConfig, int resultIndex, String... configs) { - FolderConfiguration reference = FolderConfiguration.getConfig(getFolderSegments(refConfig)); - assertNotNull(reference); - - List<? extends Configurable> list = getConfigurable(configs); - - Configurable match = reference.findMatchingConfigurable(list); - assertEquals(resultIndex, list.indexOf(match)); - } - - private List<? extends Configurable> getConfigurable(String... configs) { - ArrayList<MockConfigurable> list = new ArrayList<MockConfigurable>(); - - for (String config : configs) { - list.add(new MockConfigurable(config)); - } - - return list; - } - - private static String[] getFolderSegments(String config) { - return (config.length() > 0 ? "foo-" + config : "foo").split("-"); - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/KeyboardStateQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/KeyboardStateQualifierTest.java deleted file mode 100644 index cf52a38..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/KeyboardStateQualifierTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.resources.configuration; - -import com.android.resources.KeyboardState; - -import junit.framework.TestCase; - -public class KeyboardStateQualifierTest extends TestCase { - - private KeyboardStateQualifier ksq; - private FolderConfiguration config; - - @Override - protected void setUp() throws Exception { - super.setUp(); - ksq = new KeyboardStateQualifier(); - config = new FolderConfiguration(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - ksq = null; - config = null; - } - - public void testExposed() { - assertEquals(true, ksq.checkAndSet("keysexposed", config)); //$NON-NLS-1$ - assertTrue(config.getKeyboardStateQualifier() != null); - assertEquals(KeyboardState.EXPOSED, config.getKeyboardStateQualifier().getValue()); - assertEquals("keysexposed", config.getKeyboardStateQualifier().toString()); //$NON-NLS-1$ - } - - public void testHidden() { - assertEquals(true, ksq.checkAndSet("keyshidden", config)); //$NON-NLS-1$ - assertTrue(config.getKeyboardStateQualifier() != null); - assertEquals(KeyboardState.HIDDEN, config.getKeyboardStateQualifier().getValue()); - assertEquals("keyshidden", config.getKeyboardStateQualifier().toString()); //$NON-NLS-1$ - } - - public void testFailures() { - assertEquals(false, ksq.checkAndSet("", config));//$NON-NLS-1$ - assertEquals(false, ksq.checkAndSet("KEYSEXPOSED", config));//$NON-NLS-1$ - assertEquals(false, ksq.checkAndSet("other", config));//$NON-NLS-1$ - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/LanguageQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/LanguageQualifierTest.java deleted file mode 100644 index 2dfd65f..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/LanguageQualifierTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import junit.framework.TestCase; - -public class LanguageQualifierTest extends TestCase { - - private FolderConfiguration config; - private LanguageQualifier lq; - - @Override - public void setUp() throws Exception { - super.setUp(); - config = new FolderConfiguration(); - lq = new LanguageQualifier(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - config = null; - lq = null; - } - - public void testCheckAndSet() { - assertEquals(true, lq.checkAndSet("en", config)); //$NON-NLS-1$ - assertTrue(config.getLanguageQualifier() != null); - assertEquals("en", config.getLanguageQualifier().toString()); //$NON-NLS-1$ - - } - - public void testFailures() { - assertEquals(false, lq.checkAndSet("", config)); //$NON-NLS-1$ - assertEquals(false, lq.checkAndSet("EN", config)); //$NON-NLS-1$ - assertEquals(false, lq.checkAndSet("abc", config)); //$NON-NLS-1$ - } -} - diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/NavigationMethodQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/NavigationMethodQualifierTest.java deleted file mode 100644 index 4237dde..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/NavigationMethodQualifierTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.resources.Navigation; - -import junit.framework.TestCase; - -public class NavigationMethodQualifierTest extends TestCase { - - private FolderConfiguration config; - private NavigationMethodQualifier nmq; - - @Override - public void setUp() throws Exception { - super.setUp(); - config = new FolderConfiguration(); - nmq = new NavigationMethodQualifier(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - config = null; - nmq = null; - } - - public void testDPad() { - assertEquals(true, nmq.checkAndSet("dpad", config)); //$NON-NLS-1$ - assertTrue(config.getNavigationMethodQualifier() != null); - assertEquals(Navigation.DPAD, config.getNavigationMethodQualifier().getValue()); - assertEquals("dpad", config.getNavigationMethodQualifier().toString()); //$NON-NLS-1$ - } - - public void testTrackball() { - assertEquals(true, nmq.checkAndSet("trackball", config)); //$NON-NLS-1$ - assertTrue(config.getNavigationMethodQualifier() != null); - assertEquals(Navigation.TRACKBALL, config.getNavigationMethodQualifier().getValue()); - assertEquals("trackball", config.getNavigationMethodQualifier().toString()); //$NON-NLS-1$ - } - - public void testWheel() { - assertEquals(true, nmq.checkAndSet("wheel", config)); //$NON-NLS-1$ - assertTrue(config.getNavigationMethodQualifier() != null); - assertEquals(Navigation.WHEEL, config.getNavigationMethodQualifier().getValue()); - assertEquals("wheel", config.getNavigationMethodQualifier().toString()); //$NON-NLS-1$ - } - - public void testFailures() { - assertEquals(false, nmq.checkAndSet("", config));//$NON-NLS-1$ - assertEquals(false, nmq.checkAndSet("WHEEL", config));//$NON-NLS-1$ - assertEquals(false, nmq.checkAndSet("other", config));//$NON-NLS-1$ - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/NetworkCodeQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/NetworkCodeQualifierTest.java deleted file mode 100644 index 6896316..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/NetworkCodeQualifierTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2008 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.ide.common.resources.configuration; - -import junit.framework.TestCase; - -public class NetworkCodeQualifierTest extends TestCase { - - private NetworkCodeQualifier mncq; - private FolderConfiguration config; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mncq = new NetworkCodeQualifier(); - config = new FolderConfiguration(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - mncq = null; - config = null; - } - - public void testCheckAndSet() { - assertEquals(true, mncq.checkAndSet("mnc123", config));//$NON-NLS-1$ - assertTrue(config.getNetworkCodeQualifier() != null); - assertEquals(123, config.getNetworkCodeQualifier().getCode()); - assertEquals("mnc123", config.getNetworkCodeQualifier().toString()); //$NON-NLS-1$ - } - - public void testFailures() { - assertEquals(false, mncq.checkAndSet("", config));//$NON-NLS-1$ - assertEquals(false, mncq.checkAndSet("mnc", config));//$NON-NLS-1$ - assertEquals(false, mncq.checkAndSet("MNC123", config));//$NON-NLS-1$ - assertEquals(false, mncq.checkAndSet("123", config));//$NON-NLS-1$ - assertEquals(false, mncq.checkAndSet("mncsdf", config));//$NON-NLS-1$ - } - -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/PixelDensityQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/PixelDensityQualifierTest.java deleted file mode 100644 index 4ab493a..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/PixelDensityQualifierTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.resources.Density; - -import junit.framework.TestCase; - -public class PixelDensityQualifierTest extends TestCase { - - private DensityQualifier pdq; - private FolderConfiguration config; - - @Override - protected void setUp() throws Exception { - super.setUp(); - pdq = new DensityQualifier(); - config = new FolderConfiguration(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - pdq = null; - config = null; - } - - public void testCheckAndSet() { - assertEquals(true, pdq.checkAndSet("ldpi", config));//$NON-NLS-1$ - assertTrue(config.getDensityQualifier() != null); - assertEquals(Density.LOW, config.getDensityQualifier().getValue()); - assertEquals("ldpi", config.getDensityQualifier().toString()); //$NON-NLS-1$ - } - - public void testFailures() { - assertEquals(false, pdq.checkAndSet("", config));//$NON-NLS-1$ - assertEquals(false, pdq.checkAndSet("dpi", config));//$NON-NLS-1$ - assertEquals(false, pdq.checkAndSet("123dpi", config));//$NON-NLS-1$ - assertEquals(false, pdq.checkAndSet("123", config));//$NON-NLS-1$ - assertEquals(false, pdq.checkAndSet("sdfdpi", config));//$NON-NLS-1$ - } - - public void testIsBetterMatchThan() { - DensityQualifier ldpi = new DensityQualifier(Density.LOW); - DensityQualifier mdpi = new DensityQualifier(Density.MEDIUM); - DensityQualifier hdpi = new DensityQualifier(Density.HIGH); - DensityQualifier xhdpi = new DensityQualifier(Density.XHIGH); - - // first test that each Q is a better match than all other Qs when the ref is the same Q. - assertTrue(ldpi.isBetterMatchThan(mdpi, ldpi)); - assertTrue(ldpi.isBetterMatchThan(hdpi, ldpi)); - assertTrue(ldpi.isBetterMatchThan(xhdpi, ldpi)); - - assertTrue(mdpi.isBetterMatchThan(ldpi, mdpi)); - assertTrue(mdpi.isBetterMatchThan(hdpi, mdpi)); - assertTrue(mdpi.isBetterMatchThan(xhdpi, mdpi)); - - assertTrue(hdpi.isBetterMatchThan(ldpi, hdpi)); - assertTrue(hdpi.isBetterMatchThan(mdpi, hdpi)); - assertTrue(hdpi.isBetterMatchThan(xhdpi, hdpi)); - - assertTrue(xhdpi.isBetterMatchThan(ldpi, xhdpi)); - assertTrue(xhdpi.isBetterMatchThan(mdpi, xhdpi)); - assertTrue(xhdpi.isBetterMatchThan(hdpi, xhdpi)); - - // now test that the highest dpi is always preferable if there's no exact match - - // looking for ldpi: - assertTrue(hdpi.isBetterMatchThan(mdpi, ldpi)); - assertTrue(xhdpi.isBetterMatchThan(mdpi, ldpi)); - assertTrue(xhdpi.isBetterMatchThan(hdpi, ldpi)); - // the other way around - assertFalse(mdpi.isBetterMatchThan(hdpi, ldpi)); - assertFalse(mdpi.isBetterMatchThan(xhdpi, ldpi)); - assertFalse(hdpi.isBetterMatchThan(xhdpi, ldpi)); - - // looking for mdpi - assertTrue(hdpi.isBetterMatchThan(ldpi, mdpi)); - assertTrue(xhdpi.isBetterMatchThan(ldpi, mdpi)); - assertTrue(xhdpi.isBetterMatchThan(hdpi, mdpi)); - // the other way around - assertFalse(ldpi.isBetterMatchThan(hdpi, mdpi)); - assertFalse(ldpi.isBetterMatchThan(xhdpi, mdpi)); - assertFalse(hdpi.isBetterMatchThan(xhdpi, mdpi)); - - // looking for hdpi - assertTrue(mdpi.isBetterMatchThan(ldpi, hdpi)); - assertTrue(xhdpi.isBetterMatchThan(ldpi, hdpi)); - assertTrue(xhdpi.isBetterMatchThan(mdpi, hdpi)); - // the other way around - assertFalse(ldpi.isBetterMatchThan(mdpi, hdpi)); - assertFalse(ldpi.isBetterMatchThan(xhdpi, hdpi)); - assertFalse(mdpi.isBetterMatchThan(xhdpi, hdpi)); - - // looking for xhdpi - assertTrue(mdpi.isBetterMatchThan(ldpi, xhdpi)); - assertTrue(hdpi.isBetterMatchThan(ldpi, xhdpi)); - assertTrue(hdpi.isBetterMatchThan(mdpi, xhdpi)); - // the other way around - assertFalse(ldpi.isBetterMatchThan(mdpi, xhdpi)); - assertFalse(ldpi.isBetterMatchThan(hdpi, xhdpi)); - assertFalse(mdpi.isBetterMatchThan(hdpi, xhdpi)); - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/RegionQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/RegionQualifierTest.java deleted file mode 100644 index fc0402c..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/RegionQualifierTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import junit.framework.TestCase; - -public class RegionQualifierTest extends TestCase { - - private RegionQualifier rq; - private FolderConfiguration config; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - rq = new RegionQualifier(); - config = new FolderConfiguration(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - rq = null; - config = null; - } - - public void testCheckAndSet() { - assertEquals(true, rq.checkAndSet("rUS", config));//$NON-NLS-1$ - assertTrue(config.getRegionQualifier() != null); - assertEquals("US", config.getRegionQualifier().getValue()); //$NON-NLS-1$ - assertEquals("rUS", config.getRegionQualifier().toString()); //$NON-NLS-1$ - } - - public void testFailures() { - assertEquals(false, rq.checkAndSet("", config));//$NON-NLS-1$ - assertEquals(false, rq.checkAndSet("rus", config));//$NON-NLS-1$ - assertEquals(false, rq.checkAndSet("rUSA", config));//$NON-NLS-1$ - assertEquals(false, rq.checkAndSet("abc", config));//$NON-NLS-1$ - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifierTest.java deleted file mode 100644 index e57424f..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/ScreenDimensionQualifierTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import junit.framework.TestCase; - -public class ScreenDimensionQualifierTest extends TestCase { - - private ScreenDimensionQualifier sdq; - private FolderConfiguration config; - - @Override - protected void setUp() throws Exception { - super.setUp(); - sdq = new ScreenDimensionQualifier(); - config = new FolderConfiguration(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - sdq = null; - config = null; - } - - public void testCheckAndSet() { - assertEquals(true, sdq.checkAndSet("400x200", config));//$NON-NLS-1$ - assertTrue(config.getScreenDimensionQualifier() != null); - assertEquals(400, config.getScreenDimensionQualifier().getValue1()); - assertEquals(200, config.getScreenDimensionQualifier().getValue2()); - assertEquals("400x200", config.getScreenDimensionQualifier().toString()); //$NON-NLS-1$ - } - - public void testFailures() { - assertEquals(false, sdq.checkAndSet("", config));//$NON-NLS-1$ - assertEquals(false, sdq.checkAndSet("400X200", config));//$NON-NLS-1$ - assertEquals(false, sdq.checkAndSet("x200", config));//$NON-NLS-1$ - assertEquals(false, sdq.checkAndSet("ax200", config));//$NON-NLS-1$ - assertEquals(false, sdq.checkAndSet("400x", config));//$NON-NLS-1$ - assertEquals(false, sdq.checkAndSet("400xa", config));//$NON-NLS-1$ - assertEquals(false, sdq.checkAndSet("other", config));//$NON-NLS-1$ - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java deleted file mode 100644 index 3aac5f3..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.resources.ScreenOrientation; - -import junit.framework.TestCase; - -public class ScreenOrientationQualifierTest extends TestCase { - - private ScreenOrientationQualifier soq; - private FolderConfiguration config; - - @Override - protected void setUp() throws Exception { - super.setUp(); - soq = new ScreenOrientationQualifier(); - config = new FolderConfiguration(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - soq = null; - config = null; - } - - public void testPortrait() { - assertEquals(true, soq.checkAndSet("port", config)); //$NON-NLS-1$ - assertTrue(config.getScreenOrientationQualifier() != null); - assertEquals(ScreenOrientation.PORTRAIT, config.getScreenOrientationQualifier().getValue()); - assertEquals("port", config.getScreenOrientationQualifier().toString()); //$NON-NLS-1$ - } - - public void testLanscape() { - assertEquals(true, soq.checkAndSet("land", config)); //$NON-NLS-1$ - assertTrue(config.getScreenOrientationQualifier() != null); - assertEquals(ScreenOrientation.LANDSCAPE, - config.getScreenOrientationQualifier().getValue()); - assertEquals("land", config.getScreenOrientationQualifier().toString()); //$NON-NLS-1$ - } - - public void testSquare() { - assertEquals(true, soq.checkAndSet("square", config)); //$NON-NLS-1$ - assertTrue(config.getScreenOrientationQualifier() != null); - assertEquals(ScreenOrientation.SQUARE, - config.getScreenOrientationQualifier().getValue()); - assertEquals("square", config.getScreenOrientationQualifier().toString()); //$NON-NLS-1$ - } - - public void testFailures() { - assertEquals(false, soq.checkAndSet("", config));//$NON-NLS-1$ - assertEquals(false, soq.checkAndSet("PORT", config));//$NON-NLS-1$ - assertEquals(false, soq.checkAndSet("landscape", config));//$NON-NLS-1$ - assertEquals(false, soq.checkAndSet("portrait", config));//$NON-NLS-1$ - assertEquals(false, soq.checkAndSet("other", config));//$NON-NLS-1$ - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/ScreenSizeQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/ScreenSizeQualifierTest.java deleted file mode 100644 index b19f125..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/ScreenSizeQualifierTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.resources.configuration; - -import com.android.resources.ScreenSize; - -import junit.framework.TestCase; - -public class ScreenSizeQualifierTest extends TestCase { - - private ScreenSizeQualifier ssq; - private FolderConfiguration config; - - @Override - protected void setUp() throws Exception { - super.setUp(); - ssq = new ScreenSizeQualifier(); - config = new FolderConfiguration(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - ssq = null; - config = null; - } - - public void testSmall() { - assertEquals(true, ssq.checkAndSet("small", config)); //$NON-NLS-1$ - assertTrue(config.getScreenSizeQualifier() != null); - assertEquals(ScreenSize.SMALL, config.getScreenSizeQualifier().getValue()); - assertEquals("small", config.getScreenSizeQualifier().toString()); //$NON-NLS-1$ - } - - public void testNormal() { - assertEquals(true, ssq.checkAndSet("normal", config)); //$NON-NLS-1$ - assertTrue(config.getScreenSizeQualifier() != null); - assertEquals(ScreenSize.NORMAL, config.getScreenSizeQualifier().getValue()); - assertEquals("normal", config.getScreenSizeQualifier().toString()); //$NON-NLS-1$ - } - - public void testLarge() { - assertEquals(true, ssq.checkAndSet("large", config)); //$NON-NLS-1$ - assertTrue(config.getScreenSizeQualifier() != null); - assertEquals(ScreenSize.LARGE, config.getScreenSizeQualifier().getValue()); - assertEquals("large", config.getScreenSizeQualifier().toString()); //$NON-NLS-1$ - } - - public void testXLarge() { - assertEquals(true, ssq.checkAndSet("xlarge", config)); //$NON-NLS-1$ - assertTrue(config.getScreenSizeQualifier() != null); - assertEquals(ScreenSize.XLARGE, config.getScreenSizeQualifier().getValue()); - assertEquals("xlarge", config.getScreenSizeQualifier().toString()); //$NON-NLS-1$ - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/TextInputMethodQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/TextInputMethodQualifierTest.java deleted file mode 100644 index bc2c890..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/TextInputMethodQualifierTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.resources.Keyboard; - -import junit.framework.TestCase; - -public class TextInputMethodQualifierTest extends TestCase { - - private TextInputMethodQualifier timq; - private FolderConfiguration config; - - @Override - protected void setUp() throws Exception { - super.setUp(); - timq = new TextInputMethodQualifier(); - config = new FolderConfiguration(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - timq = null; - config = null; - } - - public void testQuerty() { - assertEquals(true, timq.checkAndSet("qwerty", config)); //$NON-NLS-1$ - assertTrue(config.getTextInputMethodQualifier() != null); - assertEquals(Keyboard.QWERTY, config.getTextInputMethodQualifier().getValue()); - assertEquals("qwerty", config.getTextInputMethodQualifier().toString()); //$NON-NLS-1$ - } - - public void test12Key() { - assertEquals(true, timq.checkAndSet("12key", config)); //$NON-NLS-1$ - assertTrue(config.getTextInputMethodQualifier() != null); - assertEquals(Keyboard.TWELVEKEY, config.getTextInputMethodQualifier().getValue()); - assertEquals("12key", config.getTextInputMethodQualifier().toString()); //$NON-NLS-1$ - } - - public void testNoKey() { - assertEquals(true, timq.checkAndSet("nokeys", config)); //$NON-NLS-1$ - assertTrue(config.getTextInputMethodQualifier() != null); - assertEquals(Keyboard.NOKEY, config.getTextInputMethodQualifier().getValue()); - assertEquals("nokeys", config.getTextInputMethodQualifier().toString()); //$NON-NLS-1$ - } - - public void testFailures() { - assertEquals(false, timq.checkAndSet("", config));//$NON-NLS-1$ - assertEquals(false, timq.checkAndSet("QWERTY", config));//$NON-NLS-1$ - assertEquals(false, timq.checkAndSet("12keys", config));//$NON-NLS-1$ - assertEquals(false, timq.checkAndSet("*12key", config));//$NON-NLS-1$ - assertEquals(false, timq.checkAndSet("other", config));//$NON-NLS-1$ - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/resources/configuration/TouchScreenQualifierTest.java b/sdk_common/tests/src/com/android/ide/common/resources/configuration/TouchScreenQualifierTest.java deleted file mode 100644 index 04f8a30..0000000 --- a/sdk_common/tests/src/com/android/ide/common/resources/configuration/TouchScreenQualifierTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.resources.configuration; - -import com.android.resources.TouchScreen; - -import junit.framework.TestCase; - -public class TouchScreenQualifierTest extends TestCase { - - private TouchScreenQualifier tsq; - private FolderConfiguration config; - - @Override - protected void setUp() throws Exception { - super.setUp(); - tsq = new TouchScreenQualifier(); - config = new FolderConfiguration(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - tsq = null; - config = null; - } - - public void testNoTouch() { - assertEquals(true, tsq.checkAndSet("notouch", config)); //$NON-NLS-1$ - assertTrue(config.getTouchTypeQualifier() != null); - assertEquals(TouchScreen.NOTOUCH, config.getTouchTypeQualifier().getValue()); - assertEquals("notouch", config.getTouchTypeQualifier().toString()); //$NON-NLS-1$ - } - - public void testFinger() { - assertEquals(true, tsq.checkAndSet("finger", config)); //$NON-NLS-1$ - assertTrue(config.getTouchTypeQualifier() != null); - assertEquals(TouchScreen.FINGER, config.getTouchTypeQualifier().getValue()); - assertEquals("finger", config.getTouchTypeQualifier().toString()); //$NON-NLS-1$ - } - - public void testStylus() { - assertEquals(true, tsq.checkAndSet("stylus", config)); //$NON-NLS-1$ - assertTrue(config.getTouchTypeQualifier() != null); - assertEquals(TouchScreen.STYLUS, config.getTouchTypeQualifier().getValue()); - assertEquals("stylus", config.getTouchTypeQualifier().toString()); //$NON-NLS-1$ - } - - public void testFailures() { - assertEquals(false, tsq.checkAndSet("", config));//$NON-NLS-1$ - assertEquals(false, tsq.checkAndSet("STYLUS", config));//$NON-NLS-1$ - assertEquals(false, tsq.checkAndSet("other", config));//$NON-NLS-1$ - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/xml/AndroidManifestParserTest.java b/sdk_common/tests/src/com/android/ide/common/xml/AndroidManifestParserTest.java deleted file mode 100644 index fee6014..0000000 --- a/sdk_common/tests/src/com/android/ide/common/xml/AndroidManifestParserTest.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2007 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.ide.common.xml; - -import com.android.ide.common.xml.ManifestData.UsesFeature; -import com.android.ide.common.xml.ManifestData.UsesLibrary; -import com.android.resources.Keyboard; -import com.android.resources.Navigation; -import com.android.resources.TouchScreen; - -import junit.framework.TestCase; - -import java.io.InputStream; - -/** - * Tests for {@link AndroidManifestParser} - */ -public class AndroidManifestParserTest extends TestCase { - private ManifestData mManifestTestApp; - private ManifestData mManifestInstrumentation; - - private static final String TESTDATA_PATH = - "/com/android/sdklib/testdata/"; //$NON-NLS-1$ - private static final String INSTRUMENTATION_XML = TESTDATA_PATH + - "AndroidManifest-instrumentation.xml"; //$NON-NLS-1$ - private static final String TESTAPP_XML = TESTDATA_PATH + - "AndroidManifest-testapp.xml"; //$NON-NLS-1$ - private static final String ACTIVITY_ALIAS_XML = TESTDATA_PATH + - "AndroidManifest-activityalias.xml"; //$NON-NLS-1$ - private static final String PACKAGE_NAME = "com.android.testapp"; //$NON-NLS-1$ - private static final Integer VERSION_CODE = 42; - private static final String ACTIVITY_NAME = "com.android.testapp.MainActivity"; //$NON-NLS-1$ - private static final String LIBRARY_NAME = "android.test.runner"; //$NON-NLS-1$ - private static final String LIBRARY_NAME2 = "android.test.runner2"; //$NON-NLS-1$ - private static final String FEATURE_NAME = "com.foo.feature"; //$NON-NLS-1$ - private static final String INSTRUMENTATION_NAME = "android.test.InstrumentationTestRunner"; //$NON-NLS-1$ - private static final String INSTRUMENTATION_TARGET = "com.android.AndroidProject"; //$NON-NLS-1$ - - @Override - protected void setUp() throws Exception { - super.setUp(); - - InputStream manifestStream = this.getClass().getResourceAsStream(TESTAPP_XML); - - mManifestTestApp = AndroidManifestParser.parse(manifestStream); - assertNotNull(mManifestTestApp); - - manifestStream = this.getClass().getResourceAsStream(INSTRUMENTATION_XML); - mManifestInstrumentation = AndroidManifestParser.parse(manifestStream); - assertNotNull(mManifestInstrumentation); - } - - public void testGetInstrumentationInformation() { - assertEquals(1, mManifestInstrumentation.getInstrumentations().length); - assertEquals(INSTRUMENTATION_NAME, - mManifestInstrumentation.getInstrumentations()[0].getName()); - assertEquals(INSTRUMENTATION_TARGET, - mManifestInstrumentation.getInstrumentations()[0].getTargetPackage()); - } - - public void testGetPackage() { - assertEquals(PACKAGE_NAME, mManifestTestApp.getPackage()); - } - - public void testGetVersionCode() { - assertEquals(VERSION_CODE, mManifestTestApp.getVersionCode()); - assertEquals(null, mManifestInstrumentation.getVersionCode()); - } - - public void testMinSdkVersion() { - assertEquals(7, mManifestTestApp.getMinSdkVersion()); - assertEquals(8, mManifestTestApp.getTargetSdkVersion()); - - assertEquals("foo", mManifestInstrumentation.getMinSdkVersionString()); - assertEquals(ManifestData.MIN_SDK_CODENAME, mManifestInstrumentation.getMinSdkVersion()); - } - - public void testGetActivities() { - assertEquals(1, mManifestTestApp.getActivities().length); - ManifestData.Activity activity = mManifestTestApp.getActivities()[0]; - assertEquals(ACTIVITY_NAME, activity.getName()); - assertTrue(activity.hasAction()); - assertTrue(activity.isHomeActivity()); - assertTrue(activity.hasAction()); - assertEquals(activity, mManifestTestApp.getActivities()[0]); - } - - public void testGetLauncherActivity() { - ManifestData.Activity activity = mManifestTestApp.getLauncherActivity(); - assertEquals(ACTIVITY_NAME, activity.getName()); - assertTrue(activity.hasAction()); - assertTrue(activity.isHomeActivity()); - } - - public void testSupportsScreen() { - ManifestData.SupportsScreens supportsScreens = - mManifestTestApp.getSupportsScreensFromManifest(); - - assertNotNull(supportsScreens); - assertEquals(Boolean.TRUE, supportsScreens.getAnyDensity()); - assertEquals(Boolean.TRUE, supportsScreens.getResizeable()); - assertEquals(Boolean.TRUE, supportsScreens.getSmallScreens()); - assertEquals(Boolean.TRUE, supportsScreens.getNormalScreens()); - assertEquals(Boolean.TRUE, supportsScreens.getLargeScreens()); - } - - public void testUsesConfiguration() { - ManifestData.UsesConfiguration usesConfig = mManifestTestApp.getUsesConfiguration(); - - assertNotNull(usesConfig); - assertEquals(Boolean.TRUE, usesConfig.getReqFiveWayNav()); - assertEquals(Navigation.NONAV, usesConfig.getReqNavigation()); - assertEquals(Boolean.TRUE, usesConfig.getReqHardKeyboard()); - assertEquals(Keyboard.TWELVEKEY, usesConfig.getReqKeyboardType()); - assertEquals(TouchScreen.FINGER, usesConfig.getReqTouchScreen()); - } - - private void assertEquals(ManifestData.Activity lhs, ManifestData.Activity rhs) { - assertTrue(lhs == rhs || (lhs != null && rhs != null)); - if (lhs != null && rhs != null) { - assertEquals(lhs.getName(), rhs.getName()); - assertEquals(lhs.isExported(), rhs.isExported()); - assertEquals(lhs.hasAction(), rhs.hasAction()); - assertEquals(lhs.isHomeActivity(), rhs.isHomeActivity()); - } - } - - public void testGetUsesLibraries() { - UsesLibrary[] libraries = mManifestTestApp.getUsesLibraries(); - - assertEquals(2, libraries.length); - assertEquals(LIBRARY_NAME, libraries[0].getName()); - assertEquals(Boolean.FALSE, libraries[0].getRequired()); - assertEquals(LIBRARY_NAME2, libraries[1].getName()); - assertEquals(Boolean.TRUE, libraries[1].getRequired()); - } - - public void testGetUsesFeatures() { - UsesFeature[] features = mManifestTestApp.getUsesFeatures(); - - assertEquals(2, features.length); - assertEquals(0x00020001, features[0].mGlEsVersion); - assertEquals(Boolean.TRUE, features[0].getRequired()); - assertEquals(FEATURE_NAME, features[1].getName()); - assertEquals(Boolean.TRUE, features[1].getRequired()); - } - - public void testGetPackageName() { - assertEquals(PACKAGE_NAME, mManifestTestApp.getPackage()); - } - - public void testActivityAlias() throws Exception { - InputStream manifestStream = this.getClass().getResourceAsStream(ACTIVITY_ALIAS_XML); - - ManifestData manifest = AndroidManifestParser.parse(manifestStream); - assertNotNull(manifest); - - assertEquals(manifest.getLauncherActivity().getName(), - "com.android.testapp.AliasActivity"); //$NON-NLS-1$ - } -} diff --git a/sdk_common/tests/src/com/android/ide/common/xml/SupportsScreensTest.java b/sdk_common/tests/src/com/android/ide/common/xml/SupportsScreensTest.java deleted file mode 100644 index aa88fc7..0000000 --- a/sdk_common/tests/src/com/android/ide/common/xml/SupportsScreensTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2010 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.ide.common.xml; - -import com.android.ide.common.xml.AndroidManifestParser; -import com.android.ide.common.xml.ManifestData; -import com.android.ide.common.xml.ManifestData.SupportsScreens; - -import java.io.InputStream; - -import junit.framework.TestCase; - -public class SupportsScreensTest extends TestCase { - - private static final String TESTDATA_PATH = - "/com/android/sdklib/testdata/"; //$NON-NLS-1$ - private static final String TESTAPP2_XML = TESTDATA_PATH + - "AndroidManifest-testapp2.xml"; //$NON-NLS-1$ - - public void testDefaultValuesApi3() { - SupportsScreens supportsScreens = SupportsScreens.getDefaultValues(3); - - assertNotNull(supportsScreens); - assertEquals(Boolean.FALSE, supportsScreens.getAnyDensity()); - assertEquals(Boolean.FALSE, supportsScreens.getResizeable()); - assertEquals(Boolean.FALSE, supportsScreens.getSmallScreens()); - assertEquals(Boolean.TRUE, supportsScreens.getNormalScreens()); - assertEquals(Boolean.FALSE, supportsScreens.getLargeScreens()); - } - - public void testDefaultValuesApi4() { - SupportsScreens supportsScreens = SupportsScreens.getDefaultValues(4); - - assertNotNull(supportsScreens); - assertEquals(Boolean.TRUE, supportsScreens.getAnyDensity()); - assertEquals(Boolean.TRUE, supportsScreens.getResizeable()); - assertEquals(Boolean.TRUE, supportsScreens.getSmallScreens()); - assertEquals(Boolean.TRUE, supportsScreens.getNormalScreens()); - assertEquals(Boolean.TRUE, supportsScreens.getLargeScreens()); - } - - public void testManifestParsing() throws Exception { - InputStream manifestStream = this.getClass().getResourceAsStream(TESTAPP2_XML); - - ManifestData data = AndroidManifestParser.parse(manifestStream); - assertNotNull(data); - - SupportsScreens supportsScreens = data.getSupportsScreensFromManifest(); - assertNotNull(supportsScreens); - assertEquals(null, supportsScreens.getAnyDensity()); - assertEquals(null, supportsScreens.getResizeable()); - assertEquals(null, supportsScreens.getSmallScreens()); - assertEquals(null, supportsScreens.getNormalScreens()); - assertEquals(Boolean.FALSE, supportsScreens.getLargeScreens()); - - supportsScreens = data.getSupportsScreensValues(); - assertNotNull(supportsScreens); - assertEquals(Boolean.TRUE, supportsScreens.getAnyDensity()); - assertEquals(Boolean.TRUE, supportsScreens.getResizeable()); - assertEquals(Boolean.TRUE, supportsScreens.getSmallScreens()); - assertEquals(Boolean.TRUE, supportsScreens.getNormalScreens()); - assertEquals(Boolean.FALSE, supportsScreens.getLargeScreens()); - } - - public void testOverlapWith() { - SupportsScreens supportsN = new SupportsScreens("false|false|false|true|false"); - SupportsScreens supportsSL = new SupportsScreens("false|false|true|false|true"); - - assertTrue(supportsN.overlapWith(supportsSL)); - assertTrue(supportsSL.overlapWith(supportsN)); - } - - public void testCompareScreenSizesWith() { - // set instance that support all combo of the three sizes. - SupportsScreens supportsS = new SupportsScreens("false|false|true|false|false"); - SupportsScreens supportsN = new SupportsScreens("false|false|false|true|false"); - SupportsScreens supportsL = new SupportsScreens("false|false|false|false|true"); - SupportsScreens supportsSL = new SupportsScreens("false|false|true|false|true"); - - assertEquals(-1, supportsS.compareScreenSizesWith(supportsN)); - assertEquals( 1, supportsN.compareScreenSizesWith(supportsS)); - assertEquals(-1, supportsN.compareScreenSizesWith(supportsL)); - assertEquals(-1, supportsS.compareScreenSizesWith(supportsL)); - - // test thrown exception for overlapWith == true - try { - supportsSL.compareScreenSizesWith(supportsN); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // success! - } - - // test thrown exception for hasStrictlyDifferentScreenSupportAs == false - try { - supportsSL.compareScreenSizesWith(supportsS); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // success! - } - - } - - public void testHasStrictlyDifferentScreenSupportAs() { - SupportsScreens supportsS = new SupportsScreens("false|false|true|false|false"); - SupportsScreens supportsN = new SupportsScreens("false|false|false|true|false"); - SupportsScreens supportsL = new SupportsScreens("false|false|false|false|true"); - - SupportsScreens supportsSN = new SupportsScreens("false|false|true|true|false"); - SupportsScreens supportsNL = new SupportsScreens("false|false|false|true|true"); - SupportsScreens supportsSL = new SupportsScreens("false|false|true|false|true"); - SupportsScreens supportsSNL = new SupportsScreens("false|false|true|true|true"); - - assertTrue(supportsS.hasStrictlyDifferentScreenSupportAs(supportsN)); - assertTrue(supportsS.hasStrictlyDifferentScreenSupportAs(supportsL)); - assertTrue(supportsN.hasStrictlyDifferentScreenSupportAs(supportsS)); - assertTrue(supportsN.hasStrictlyDifferentScreenSupportAs(supportsL)); - assertTrue(supportsL.hasStrictlyDifferentScreenSupportAs(supportsS)); - assertTrue(supportsL.hasStrictlyDifferentScreenSupportAs(supportsN)); - - - assertFalse(supportsSN.hasStrictlyDifferentScreenSupportAs(supportsS)); - assertFalse(supportsSN.hasStrictlyDifferentScreenSupportAs(supportsN)); - assertTrue(supportsSN.hasStrictlyDifferentScreenSupportAs(supportsL)); - assertFalse(supportsSL.hasStrictlyDifferentScreenSupportAs(supportsS)); - assertTrue(supportsSL.hasStrictlyDifferentScreenSupportAs(supportsN)); - assertFalse(supportsSL.hasStrictlyDifferentScreenSupportAs(supportsL)); - assertTrue(supportsNL.hasStrictlyDifferentScreenSupportAs(supportsS)); - assertFalse(supportsNL.hasStrictlyDifferentScreenSupportAs(supportsN)); - assertFalse(supportsNL.hasStrictlyDifferentScreenSupportAs(supportsL)); - assertFalse(supportsSNL.hasStrictlyDifferentScreenSupportAs(supportsS)); - assertFalse(supportsSNL.hasStrictlyDifferentScreenSupportAs(supportsN)); - assertFalse(supportsSNL.hasStrictlyDifferentScreenSupportAs(supportsL)); - } -} diff --git a/sdkmanager/app/.classpath b/sdkmanager/app/.classpath index b06fd5a..98cdeb1 100644 --- a/sdkmanager/app/.classpath +++ b/sdkmanager/app/.classpath @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry excluding="**/Android.mk" kind="src" path="src"/> - <classpathentry excluding="**/Android.mk" kind="src" path="tests"/> + <classpathentry excluding="**/Android.mk" kind="src" path="src/main/java"/> + <classpathentry excluding="**/Android.mk" kind="src" path="src/test/java"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/sdklib"/> <classpathentry combineaccessrules="false" kind="src" path="/sdkuilib"/> @@ -11,5 +11,6 @@ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-13.0.1.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/sdkmanager/app/Android.mk b/sdkmanager/app/Android.mk index 82aad43..f72c401 100644 --- a/sdkmanager/app/Android.mk +++ b/sdkmanager/app/Android.mk @@ -1,10 +1,22 @@ # Copyright 2007 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. +# LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_RESOURCE_DIRS := src +LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java) +LOCAL_JAVA_RESOURCE_DIRS := src/main/java LOCAL_JAR_MANIFEST := etc/manifest.txt @@ -17,6 +29,7 @@ LOCAL_JAR_MANIFEST := etc/manifest.txt # current VM is 32 or 64 bit.) LOCAL_JAVA_LIBRARIES := \ common \ + guava-tools \ sdklib \ sdkuilib \ swt \ @@ -25,10 +38,33 @@ LOCAL_JAVA_LIBRARIES := \ org.eclipse.core.commands_3.6.0.I20100512-1500 LOCAL_MODULE := sdkmanager -LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_TAGS := optional include $(BUILD_HOST_JAVA_LIBRARY) # Build all sub-directories include $(call all-makefiles-under,$(LOCAL_PATH)) + +# ----- TESTS ------ +# Copyright (C) 2011 The Android Open Source Project + +include $(CLEAR_VARS) + +# Only compile source java files in this lib. +LOCAL_SRC_FILES := $(call all-java-files-under, src/test/java) + +LOCAL_MODULE := sdkmanager-tests +LOCAL_MODULE_TAGS := optional + +LOCAL_JAVA_LIBRARIES := \ + guava-tools \ + httpclient-4.1.1 \ + httpcore-4.1 \ + httpmime-4.1.1 \ + junit \ + sdkmanager \ + sdklib + +include $(BUILD_HOST_JAVA_LIBRARY) + diff --git a/sdkmanager/app/etc/Android.mk b/sdkmanager/app/etc/Android.mk index 96ec4df..8e3786e 100644 --- a/sdkmanager/app/etc/Android.mk +++ b/sdkmanager/app/etc/Android.mk @@ -4,6 +4,6 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_PREBUILT_EXECUTABLES := android -LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_TAGS := optional include $(BUILD_HOST_PREBUILT) diff --git a/sdkmanager/app/etc/manifest.txt b/sdkmanager/app/etc/manifest.txt index 50e1285..44be3b8 100644 --- a/sdkmanager/app/etc/manifest.txt +++ b/sdkmanager/app/etc/manifest.txt @@ -1,2 +1,2 @@ Main-Class: com.android.sdkmanager.Main -Class-Path: common.jar sdklib.jar sdkuilib.jar swtmenubar.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.equinox.common_3.6.0.v20100503.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar +Class-Path: common.jar sdklib.jar sdkuilib.jar swtmenubar.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.equinox.common_3.6.0.v20100503.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar guava-tools.jar diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/main/java/com/android/sdkmanager/Main.java index 8f58612..0ef18b8 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/sdkmanager/app/src/main/java/com/android/sdkmanager/Main.java @@ -40,9 +40,9 @@ import com.android.sdklib.internal.repository.DownloadCache; import com.android.sdklib.internal.repository.DownloadCache.Strategy; import com.android.sdklib.internal.repository.packages.PlatformToolPackage; import com.android.sdklib.internal.repository.packages.ToolPackage; +import com.android.sdklib.internal.repository.updater.SdkUpdaterNoWindow; import com.android.sdklib.repository.SdkAddonConstants; import com.android.sdklib.repository.SdkRepoConstants; -import com.android.sdkuilib.internal.repository.SdkUpdaterNoWindow; import com.android.sdkuilib.internal.widgets.MessageBoxLog; import com.android.sdkuilib.repository.AvdManagerWindow; import com.android.sdkuilib.repository.AvdManagerWindow.AvdInvocationContext; @@ -1512,6 +1512,7 @@ public class Main { /** * Reads a line from the input stream, masking it as much as possible. */ + @SuppressWarnings("unused") private String promptPassword(String prompt) throws IOException { // Setup a thread that tries to overwrite any input by diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/main/java/com/android/sdkmanager/SdkCommandLine.java index 6a74c29..6a74c29 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java +++ b/sdkmanager/app/src/main/java/com/android/sdkmanager/SdkCommandLine.java diff --git a/sdkmanager/app/tests/com/android/sdklib/SdkManagerTestCase.java b/sdkmanager/app/src/test/java/com/android/sdklib/SdkManagerTestCase.java index ab45785..ab45785 100755 --- a/sdkmanager/app/tests/com/android/sdklib/SdkManagerTestCase.java +++ b/sdkmanager/app/src/test/java/com/android/sdklib/SdkManagerTestCase.java diff --git a/sdkmanager/app/tests/com/android/sdklib/mock/MockLog.java b/sdkmanager/app/src/test/java/com/android/sdklib/mock/MockLog.java index e64ed51..e64ed51 100644 --- a/sdkmanager/app/tests/com/android/sdklib/mock/MockLog.java +++ b/sdkmanager/app/src/test/java/com/android/sdklib/mock/MockLog.java diff --git a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java b/sdkmanager/app/src/test/java/com/android/sdkmanager/AvdManagerTest.java index a376490..a376490 100644 --- a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java +++ b/sdkmanager/app/src/test/java/com/android/sdkmanager/AvdManagerTest.java diff --git a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java b/sdkmanager/app/src/test/java/com/android/sdkmanager/MainTest.java index 24a644b..24a644b 100644 --- a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java +++ b/sdkmanager/app/src/test/java/com/android/sdkmanager/MainTest.java diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java b/sdkmanager/app/src/test/java/com/android/sdkmanager/SdkCommandLineTest.java index e88d892..e88d892 100644 --- a/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java +++ b/sdkmanager/app/src/test/java/com/android/sdkmanager/SdkCommandLineTest.java diff --git a/sdkmanager/app/tests/Android.mk b/sdkmanager/app/tests/Android.mk deleted file mode 100644 index 7b88129..0000000 --- a/sdkmanager/app/tests/Android.mk +++ /dev/null @@ -1,34 +0,0 @@ -# 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. - -LOCAL_PATH := $(call my-dir) - - -include $(CLEAR_VARS) - -# Only compile source java files in this lib. -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_MODULE := sdkmanager-tests -LOCAL_MODULE_TAGS := optional - -LOCAL_JAVA_LIBRARIES := \ - httpclient-4.1.1 \ - httpcore-4.1 \ - httpmime-4.1.1 \ - junit \ - sdkmanager \ - sdklib - -include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/sdkmanager/libs/sdklib/Android.mk b/sdkmanager/libs/sdklib/Android.mk index dfd3fcb..ef0362d 100644 --- a/sdkmanager/libs/sdklib/Android.mk +++ b/sdkmanager/libs/sdklib/Android.mk @@ -24,10 +24,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := sdklib LOCAL_MODULE_TAGS := optional -# IMPORTANT: if you add a new dependency here, please make sure -# to also check the following files: -# sdkmanager/sdklib/manifest.txt -# sdkmanager/app/etc/android.bat + LOCAL_JAVA_LIBRARIES := \ common \ commons-codec-1.4 \ @@ -42,7 +39,7 @@ LOCAL_JAVA_LIBRARIES := \ layoutlib_api LOCAL_PREBUILT_JAVA_LIBRARIES := \ - ../../../../prebuilts/devtools/$(LOCAL_MODULE)/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) + ../../../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) include $(BUILD_HOST_PREBUILT) diff --git a/sdkmanager/libs/sdkuilib/Android.mk b/sdkmanager/libs/sdkuilib/Android.mk index b746632..df35432 100644 --- a/sdkmanager/libs/sdkuilib/Android.mk +++ b/sdkmanager/libs/sdkuilib/Android.mk @@ -24,10 +24,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := sdkuilib LOCAL_MODULE_TAGS := optional -# IMPORTANT: if you add a new dependency here, please make sure -# to also check the following file: -# sdkmanager/app/etc/android.bat -# (Note: there is no manifest.txt for sdkuilib.) + LOCAL_JAVA_LIBRARIES := \ common \ commons-codec-1.4 \ @@ -45,7 +42,7 @@ LOCAL_JAVA_LIBRARIES := \ swtmenubar LOCAL_PREBUILT_JAVA_LIBRARIES := \ - ../../../../prebuilts/devtools/$(LOCAL_MODULE)/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) + ../../../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) include $(BUILD_HOST_PREBUILT) diff --git a/swtmenubar/Android.mk b/swtmenubar/Android.mk index 865c277..01e8645 100644 --- a/swtmenubar/Android.mk +++ b/swtmenubar/Android.mk @@ -26,7 +26,7 @@ LOCAL_JAVA_LIBRARIES := \ org.eclipse.jface_3.6.2.M20110210-1200 LOCAL_PREBUILT_JAVA_LIBRARIES := \ - ../../prebuilts/devtools/$(LOCAL_MODULE)/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) + ../../prebuilts/devtools/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX) include $(BUILD_HOST_PREBUILT) diff --git a/traceview/etc/manifest.txt b/traceview/etc/manifest.txt index 0c17bec..e145b4a 100644 --- a/traceview/etc/manifest.txt +++ b/traceview/etc/manifest.txt @@ -1,2 +1,2 @@ Main-Class: com.android.traceview.MainWindow -Class-Path: common.jar swt.jar org.eclipse.equinox.common_3.2.0.v20060603.jar org.eclipse.jface_3.2.0.I20060605-1400.jar org.eclipse.core.commands_3.2.0.I20060605-1400.jar +Class-Path: common.jar swt.jar org.eclipse.equinox.common_3.2.0.v20060603.jar org.eclipse.jface_3.2.0.I20060605-1400.jar org.eclipse.core.commands_3.2.0.I20060605-1400.jar guava-tools.jar |