If I simply placed the source in the same folder as the class the below code worked fine for both code in the classpath and in jars
Note that there is no reason to prefix the name with "/", but you should scan for top level class.
I apologize for it being in core java but I didn't want to add any additional dependencies and wanted to be as clear as possible.
package com.stackoverflow.q53749060;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Arrays;
import java.util.stream.Stream;
import org.junit.Test;
import com.stackoverflow.q53749060.Answer.Result.Found;
import com.stackoverflow.q53749060.Answer.Result.NotFound;
import com.stackoverflow.q53749060.MyTopLevelClass.MyNestedClass;
import com.stackoverflow.q53749060.MyTopLevelClassInAnotherJar.MyNestedClassInAnotherJar;
@SuppressWarnings("javadoc")
public class Answer {
static final String[] EXTENSIONS = { "java", "scala" };
@Test
public void test() {
Arrays.stream(EXTENSIONS)
.flatMap(ext -> toSource(ext, MyTopLevelClass.class, MyNestedClass.class,MyTopLevelClassInAnotherJar.class,MyNestedClassInAnotherJar.class, String.class))
.forEach(System.out::println);
}
public Stream<Result> toSource(final String extension, final Class<?>... classes) {
return Arrays.stream(classes)
.map(clazz -> toSource(extension, clazz));
}
public Result toSource(final String extension, final Class<?> clazz) {
Class<?> topLevelClass = clazz;
while (topLevelClass.getEnclosingClass() != null) {
topLevelClass = topLevelClass.getEnclosingClass();
}
final String name = topLevelClass.getName()
.replaceAll("\\.", "/") + "." + extension;
final Thread currentThread = Thread.currentThread();
final ClassLoader contextClassLoader = currentThread.getContextClassLoader();
if (contextClassLoader.getResource(name) == null) {
return new NotFound(clazz);
}
final String source = toSource(name, contextClassLoader);
return new Found(clazz, name, source);
}
public String toSource(final String name, final ClassLoader contextClassLoader) {
try (final InputStream resourceInputStream = contextClassLoader.getResourceAsStream(name);
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
int length;
byte[] data = new byte[1024];
while ((length = resourceInputStream.read(data, 0, data.length)) != -1) {
byteArrayOutputStream.write(data, 0, length);
}
byteArrayOutputStream.flush();
byte[] byteArray = byteArrayOutputStream.toByteArray();
return new String(byteArray);
} catch (IOException ioe) {
throw new UncheckedIOException("Failed to read source file: " + name, ioe);
}
}
static class Result {
final Class<?> clazz;
Result(Class<?> clazz) {
super();
this.clazz = clazz;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.clazz == null) ? 0 : this.clazz.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Result other = (Result) obj;
if (this.clazz == null) {
if (other.clazz != null) {
return false;
}
} else if (!this.clazz.equals(other.clazz)) {
return false;
}
return true;
}
@Override
public String toString() {
return "Result [clazz=" + this.clazz + "]";
}
static class Found extends Result {
final String source;
final String path;
Found(Class<?> clazz, String path, String source) {
super(clazz);
this.path = path;
this.source = source;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((this.source == null) ? 0 : this.source.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Found other = (Found) obj;
if (this.source == null) {
if (other.source != null) {
return false;
}
} else if (!this.source.equals(other.source)) {
return false;
}
return true;
}
@Override
public String toString() {
return "Found [source=" + this.source + ", clazz=" + this.clazz + "]";
}
}
static class NotFound extends Result {
NotFound(Class<?> clazz) {
super(clazz);
}
@Override
public String toString() {
return "NotFound [clazz=" + this.clazz + "]";
}
}
}
}