InMemoryModuleFinder.java

/*-
 * #%L
 * io.earcam.instrumental.lade.jpms
 * %%
 * Copyright (C) 2018 earcam
 * %%
 * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
 * 
 * You <b>must</b> choose to accept, in full - any individual or combination of 
 * the following licenses:
 * <ul>
 * 	<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
 * 	<li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
 * 	<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
 * 	<li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
 * </ul>
 * #L%
 */
package io.earcam.instrumental.lade.jpms;

import java.io.IOException;
import java.io.InputStream;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.net.URI;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import io.earcam.instrumental.lade.InMemoryClassLoader;
import io.earcam.unexceptional.CheckedFunction;
import io.earcam.unexceptional.Closing;
import io.earcam.unexceptional.Exceptional;

public final class InMemoryModuleFinder implements ModuleFinder {

	private static final String MODULE_INFO_CLASS = "module-info.class";
	private final InMemoryClassLoader loader;


	public InMemoryModuleFinder(InMemoryClassLoader loader)
	{
		this.loader = loader;
	}


	@Override
	public Optional<ModuleReference> find(String name)
	{
		return findAll().stream()
				.filter(m -> name.equals(m.descriptor().name()))
				.findAny();
	}


	@Override
	public Set<ModuleReference> findAll()
	{
		Enumeration<URL> resources = loader.getResources(MODULE_INFO_CLASS, false);

		Set<ModuleReference> references = new HashSet<>();
		while(resources.hasMoreElements()) {
			URL moduleInfo = resources.nextElement();
			ModuleDescriptor descriptor = parseModuleInfoByteCode(moduleInfo);
			URI uri = extractBaseUri(moduleInfo);
			Set<String> list = resourcesForThisModule(uri);
			references.add(new InMemoryModuleReference(descriptor, uri, list));

		}
		return references;
	}


	private Set<String> resourcesForThisModule(URI baseUri)
	{
		return loader.resources()
				.map(Object::toString)
				.filter(r -> r.startsWith(baseUri.toString()))
				.collect(Collectors.toSet());
	}


	private URI extractBaseUri(URL moduleInfo)
	{
		String url = moduleInfo.toString();
		url = url.substring(0, url.length() - MODULE_INFO_CLASS.length());
		return Exceptional.uri(url);
	}


	private ModuleDescriptor parseModuleInfoByteCode(URL moduleInfo)
	{
		CheckedFunction<InputStream, ModuleDescriptor, IOException> converter = ModuleDescriptor::read;
		return Closing.closeAfterApplying(
				URL::openStream,
				moduleInfo,
				converter);
	}
}