All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.tools.lint.Reporter Maven / Gradle / Ivy

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.lint;

import static com.android.SdkConstants.CURRENT_PLATFORM;
import static com.android.SdkConstants.DOT_9PNG;
import static com.android.SdkConstants.DOT_PNG;
import static com.android.SdkConstants.PLATFORM_LINUX;
import static com.android.tools.lint.detector.api.LintUtils.endsWith;
import static java.io.File.separatorChar;

import com.android.annotations.Nullable;
import com.android.utils.SdkUtils;
import com.google.common.annotations.Beta;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
import com.google.common.io.Files;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/** A reporter is an output generator for lint warnings
 * 

* NOTE: This is not a public or final API; if you rely on this be prepared * to adjust your code for the next tools release. */ @Beta public abstract class Reporter { protected final LintCliClient mClient; protected final File mOutput; protected String mTitle = "Lint Report"; protected boolean mSimpleFormat; protected boolean mBundleResources; protected Map mUrlMap; protected File mResources; protected final Map mResourceUrl = new HashMap(); protected final Map mNameToFile = new HashMap(); protected boolean mDisplayEmpty = true; /** * Write the given warnings into the report * * @param errorCount the number of errors * @param warningCount the number of warnings * @param issues the issues to be reported * @throws IOException if an error occurs */ public abstract void write(int errorCount, int warningCount, List issues) throws IOException; protected Reporter(LintCliClient client, File output) { mClient = client; mOutput = output; } /** * Sets the report title * * @param title the title of the report */ public void setTitle(String title) { mTitle = title; } /** @return the title of the report */ public String getTitle() { return mTitle; } /** * Sets whether the report should bundle up resources along with the HTML report. * This implies a non-simple format (see {@link #setSimpleFormat(boolean)}). * * @param bundleResources if true, copy images into a directory relative to * the report */ public void setBundleResources(boolean bundleResources) { mBundleResources = bundleResources; mSimpleFormat = false; } /** * Sets whether the report should use simple formatting (meaning no JavaScript, * embedded images, etc). * * @param simpleFormat whether the formatting should be simple */ public void setSimpleFormat(boolean simpleFormat) { mSimpleFormat = simpleFormat; } /** * Returns whether the report should use simple formatting (meaning no JavaScript, * embedded images, etc). * * @return whether the report should use simple formatting */ public boolean isSimpleFormat() { return mSimpleFormat; } String getUrl(File file) { if (mBundleResources && !mSimpleFormat) { String url = getRelativeResourceUrl(file); if (url != null) { return url; } } if (mUrlMap != null) { String path = file.getAbsolutePath(); // Perform the comparison using URLs such that we properly escape spaces etc. String pathUrl = encodeUrl(path); for (Map.Entry entry : mUrlMap.entrySet()) { String prefix = entry.getKey(); String prefixUrl = encodeUrl(prefix); if (pathUrl.startsWith(prefixUrl)) { String relative = pathUrl.substring(prefixUrl.length()); return entry.getValue() + relative; } } } if (file.isAbsolute()) { String relativePath = getRelativePath(mOutput.getParentFile(), file); if (relativePath != null) { relativePath = relativePath.replace(separatorChar, '/'); return encodeUrl(relativePath); } } try { return SdkUtils.fileToUrlString(file); } catch (MalformedURLException e) { return null; } } /** Encodes the given String as a safe URL substring, escaping spaces etc */ static String encodeUrl(String url) { try { url = url.replace('\\', '/'); return URLEncoder.encode(url, "UTF-8").replace("%2F", "/"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { // This shouldn't happen for UTF-8 System.err.println("Invalid string " + e.getLocalizedMessage()); return url; } } /** Set mapping of path prefixes to corresponding URLs in the HTML report */ public void setUrlMap(@Nullable Map urlMap) { mUrlMap = urlMap; } /** Gets a pointer to the local resource directory, if any */ File getResourceDir() { if (mResources == null && mBundleResources) { mResources = computeResourceDir(); if (mResources == null) { mBundleResources = false; } } return mResources; } /** Finds/creates the local resource directory, if possible */ File computeResourceDir() { String fileName = mOutput.getName(); int dot = fileName.indexOf('.'); if (dot != -1) { fileName = fileName.substring(0, dot); } File resources = new File(mOutput.getParentFile(), fileName + "_files"); //$NON-NLS-1$ if (!resources.exists() && !resources.mkdir()) { resources = null; } return resources; } /** Returns a URL to a local copy of the given file, or null */ protected String getRelativeResourceUrl(File file) { String resource = mResourceUrl.get(file); if (resource != null) { return resource; } String name = file.getName(); if (!endsWith(name, DOT_PNG) || endsWith(name, DOT_9PNG)) { return null; } // Attempt to make local copy File resourceDir = getResourceDir(); if (resourceDir != null) { String base = file.getName(); File path = mNameToFile.get(base); if (path != null && !path.equals(file)) { // That filename already exists and is associated with a different path: // make a new unique version for (int i = 0; i < 100; i++) { base = '_' + base; path = mNameToFile.get(base); if (path == null || path.equals(file)) { break; } } } File target = new File(resourceDir, base); try { Files.copy(file, target); } catch (IOException e) { return null; } return resourceDir.getName() + '/' + encodeUrl(base); } return null; } /** Returns a URL to a local copy of the given resource, or null. There is * no filename conflict resolution. */ protected String addLocalResources(URL url) { // Attempt to make local copy File resourceDir = computeResourceDir(); if (resourceDir != null) { String base = url.getFile(); base = base.substring(base.lastIndexOf('/') + 1); mNameToFile.put(base, new File(url.toExternalForm())); File target = new File(resourceDir, base); try { FileOutputStream output = new FileOutputStream(target); InputStream input = url.openStream(); ByteStreams.copy(input, output); Closeables.closeQuietly(output); Closeables.closeQuietly(input); } catch (IOException e) { return null; } return resourceDir.getName() + '/' + encodeUrl(base); } return null; } // Based on similar code in com.intellij.openapi.util.io.FileUtilRt @Nullable static String getRelativePath(File base, File file) { if (base == null || file == null) { return null; } if (!base.isDirectory()) { base = base.getParentFile(); if (base == null) { return null; } } if (base.equals(file)) { return "."; } final String filePath = file.getAbsolutePath(); String basePath = base.getAbsolutePath(); // TODO: Make this return null if we go all the way to the root! basePath = !basePath.isEmpty() && basePath.charAt(basePath.length() - 1) == separatorChar ? basePath : basePath + separatorChar; // Whether filesystem is case sensitive. Technically on OSX you could create a // sensitive one, but it's not the default. boolean caseSensitive = CURRENT_PLATFORM == PLATFORM_LINUX; Locale l = Locale.getDefault(); String basePathToCompare = caseSensitive ? basePath : basePath.toLowerCase(l); String filePathToCompare = caseSensitive ? filePath : filePath.toLowerCase(l); if (basePathToCompare.equals(!filePathToCompare.isEmpty() && filePathToCompare.charAt(filePathToCompare.length() - 1) == separatorChar ? filePathToCompare : filePathToCompare + separatorChar)) { return "."; } int len = 0; int lastSeparatorIndex = 0; // bug in inspection; see http://youtrack.jetbrains.com/issue/IDEA-118971 //noinspection ConstantConditions while (len < filePath.length() && len < basePath.length() && filePathToCompare.charAt(len) == basePathToCompare.charAt(len)) { if (basePath.charAt(len) == separatorChar) { lastSeparatorIndex = len; } len++; } if (len == 0) { return null; } StringBuilder relativePath = new StringBuilder(); for (int i = len; i < basePath.length(); i++) { if (basePath.charAt(i) == separatorChar) { relativePath.append(".."); relativePath.append(separatorChar); } } relativePath.append(filePath.substring(lastSeparatorIndex + 1)); return relativePath.toString(); } /** * Returns whether this report should display info (such as a path to the report) if * no issues were found */ public boolean isDisplayEmpty() { return mDisplayEmpty; } /** * Sets whether this report should display info (such as a path to the report) if * no issues were found */ public void setDisplayEmpty(boolean displayEmpty) { mDisplayEmpty = displayEmpty; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy