ConfigurationModel.java
/*-
* #%L
* io.earcam.utilitarian.site.search.offline
* %%
* Copyright (C) 2017 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.utilitarian.site.search.offline;
import static io.earcam.utilitarian.site.search.offline.Crawler.crawler;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.StreamSupport.stream;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Stream;
public abstract class ConfigurationModel {
public static class Step extends ConfigurationModel {}
public static class Mapping {
private File dir;
private URI uri;
public File getDir()
{
return dir;
}
static Path dirAsPath(Mapping mapping)
{
return mapping.getDir().toPath();
}
public void setDir(File dir)
{
this.dir = dir;
}
public URI getUri()
{
return uri;
}
public void setUri(URI uri)
{
this.uri = uri;
}
}
public static class Indexing extends ConfigurationModel {
public Indexer build()
{
return create(Indexer.class, this);
}
}
static <T extends Component> T create(Class<T> type, ConfigurationModel model)
{
T component = serviceById(type, model.getId());
component.configure(model.getConfiguration());
return component;
}
private static <S extends Component> S serviceById(Class<S> service, String id)
{
return serviceStream(service)
.filter(s -> id.equals(s.id()))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Not SPI service with id '" + id
+ "' found of type '" + service.getCanonicalName() + "'"));
}
private static <S extends Component> Stream<S> serviceStream(Class<S> service)
{
return stream(ServiceLoader.load(service).spliterator(), false);
}
public static class Crawling {
private List<Mapping> mappings;
private List<Step> steps;
public List<Mapping> getMappings()
{
return mappings;
}
public void setMappings(List<Mapping> mappings)
{
this.mappings = mappings;
}
public List<Step> getSteps()
{
return steps;
}
public void setSteps(List<Step> steps)
{
this.steps = steps;
}
public Crawler build()
{
Crawler crawler = crawler(mappings.stream()
.collect(toMap(Mapping::dirAsPath, Mapping::getUri)));
Set<String> filters = serviceIds(Filter.class);
Set<String> processors = serviceIds(Processor.class);
for(Step step : steps) {
if(filters.contains(step.getId())) {
crawler.filter(create(Filter.class, step));
} else if(processors.contains(step.getId())) {
crawler.processor(create(Processor.class, step));
} else {
throw new IllegalStateException("No filter or processor found via SPI with ID '" + step.getId() + "'");
}
}
return crawler;
}
private static <S extends Component> Set<String> serviceIds(Class<S> service)
{
return serviceStream(service)
.map(Component::id)
.collect(toSet());
}
}
private String id;
private Map<String, String> configuration;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public Map<String, String> getConfiguration()
{
return configuration;
}
public void setConfiguration(Map<String, String> configuration)
{
this.configuration = configuration;
}
}