/*
* Copyright 2008-2017 by Emeric Vernat
*
* This file is part of Java Melody.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.bull.javamelody;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.security.CodeSource;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
/**
* Rapport html pour afficher un fichier source java.
* @author Emeric Vernat
*/
class HtmlSourceReport extends HtmlAbstractReport {
private static final File JDK_SRC_FILE = getJdkSrcFile();
private final String source;
HtmlSourceReport(String className, Writer writer) throws IOException {
super(writer);
if (className.indexOf('$') == -1) {
this.source = getSource(className);
} else {
this.source = getSource(className.substring(0, className.indexOf('$')));
}
}
private String getSource(String className) throws IOException {
final Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (final ClassNotFoundException e) {
return null;
}
final CodeSource codeSource = clazz.getProtectionDomain().getCodeSource();
if (clazz.getName().startsWith("java.")
|| clazz.getName().startsWith("javax.") && codeSource == null) {
if (JDK_SRC_FILE != null) {
return getSourceFromJar(clazz, JDK_SRC_FILE);
}
} else if (codeSource != null) {
final File sourceJarFile = MavenArtifact.getSourceJarFile(codeSource.getLocation());
if (sourceJarFile != null) {
return getSourceFromJar(clazz, sourceJarFile);
}
}
return null;
}
private static File getJdkSrcFile() {
File file = new File(System.getProperty("java.home"));
if ("jre".equalsIgnoreCase(file.getName())) {
file = file.getParentFile();
}
final File srcZipFile = new File(file, "src.zip");
if (srcZipFile.exists()) {
return srcZipFile;
}
return null;
}
private String getSourceFromJar(Class<?> clazz, File srcJarFile)
throws ZipException, IOException {
final ZipFile zipFile = new ZipFile(srcJarFile);
try {
final ZipEntry entry = zipFile.getEntry(clazz.getName().replace('.', '/') + ".java");
if (entry == null) {
return null;
}
final StringWriter writer = new StringWriter();
final InputStream inputStream = zipFile.getInputStream(entry);
try {
final Reader reader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
try {
final char[] chars = new char[1024];
int read = reader.read(chars);
while (read != -1) {
writer.write(chars, 0, read);
read = reader.read(chars);
}
} finally {
reader.close();
}
} finally {
inputStream.close();
}
return writer.toString();
} finally {
zipFile.close();
}
}
@Override
void toHtml() throws IOException {
if (source != null) {
String html = JavaHTMLizer.htmlize(source);
html = JavaHTMLizer.addLineNumbers(html);
writeDirectly("<code>");
writeDirectly(html);
writeDirectly("</code>");
} else {
write("#source_not_found#");
}
}
static String htmlEncodeStackTraceElement(String element) {
if (element.endsWith(")") && !element.endsWith("(Native Method)")
&& !element.endsWith("(Unknown Source)")) {
final int index3 = element.lastIndexOf(':');
final int index2 = element.lastIndexOf('(');
final int index1 = element.lastIndexOf('.', index2);
final int index0 = element.lastIndexOf(' ', index1);
if (index1 > index0 && index2 != -1 && index3 > index2) {
final String classNameEncoded = urlEncode(element.substring(index0 + 1, index1));
return htmlEncodeButNotSpace(element.substring(0, index2 + 1)) + "<a href='?part="
+ HttpParameters.SOURCE_PART + "&class=" + classNameEncoded + '#'
+ urlEncode(element.substring(index3 + 1, element.length() - 1))
+ "' class='lightwindow' type='external' title='" + classNameEncoded + "'>"
+ htmlEncode(element.substring(index2 + 1, element.length() - 1)) + "</a>)";
}
}
return htmlEncodeButNotSpace(element);
}
static String addLinkToClassName(String className) {
String cleanClassName = className;
if (cleanClassName.endsWith("[]")) {
cleanClassName = cleanClassName.substring(0, cleanClassName.length() - 2);
}
return "<a href='?part=" + HttpParameters.SOURCE_PART + "&class=" + cleanClassName
+ "' class='lightwindow' type='external' title='" + cleanClassName + "'>"
+ className + "</a>";
}
}