blob: 663fa02e0af21c53472826bfacb28b753fdbc2bd [file] [log] [blame]
/*
* 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.DOT_9PNG;
import static com.android.SdkConstants.DOT_PNG;
import static com.android.tools.lint.detector.api.LintUtils.endsWith;
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.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** A reporter is an output generator for lint warnings
* <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 Reporter {
protected final LintCliClient mClient;
protected final File mOutput;
protected String mTitle = "Lint Report";
protected boolean mSimpleFormat;
protected boolean mBundleResources;
protected Map<String, String> mUrlMap;
protected File mResources;
protected final Map<File, String> mResourceUrl = new HashMap<File, String>();
protected final Map<String, File> mNameToFile = new HashMap<String, File>();
/**
* 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<Warning> 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();
try {
// Perform the comparison using URLs such that we properly escape spaces etc.
String pathUrl = URLEncoder.encode(path, "UTF-8"); //$NON-NLS-1$
for (Map.Entry<String, String> entry : mUrlMap.entrySet()) {
String prefix = entry.getKey();
String prefixUrl = URLEncoder.encode(prefix, "UTF-8"); //$NON-NLS-1$
if (pathUrl.startsWith(prefixUrl)) {
String relative = pathUrl.substring(prefixUrl.length());
return entry.getValue()
+ relative.replace("%2F", "/"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
} catch (UnsupportedEncodingException e) {
// This shouldn't happen for UTF-8
System.err.println("Invalid URL map specification - " + e.getLocalizedMessage());
}
}
return null;
}
/** Encodes the given String as a safe URL substring, escaping spaces etc */
static String encodeUrl(String url) {
try {
return URLEncoder.encode(url, "UTF-8"); //$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(Map<String, String> 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;
}
}