/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.common.headless.impl;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.Strings;
import de.fhg.igd.slf4jplus.ALogger;
import de.fhg.igd.slf4jplus.ALoggerFactory;
import eu.esdihumboldt.hale.common.align.model.Alignment;
import eu.esdihumboldt.hale.common.align.service.FunctionService;
import eu.esdihumboldt.hale.common.align.service.TransformationFunctionService;
import eu.esdihumboldt.hale.common.align.service.impl.AlignmentFunctionService;
import eu.esdihumboldt.hale.common.align.service.impl.AlignmentTransformationFunctionService;
import eu.esdihumboldt.hale.common.align.transformation.service.TransformationSchemas;
import eu.esdihumboldt.hale.common.codelist.service.CodeListRegistry;
import eu.esdihumboldt.hale.common.core.io.HaleIO;
import eu.esdihumboldt.hale.common.core.io.IOAdvisor;
import eu.esdihumboldt.hale.common.core.io.IOProviderConfigurationException;
import eu.esdihumboldt.hale.common.core.io.extension.IOProviderDescriptor;
import eu.esdihumboldt.hale.common.core.io.project.FixedProjectInfoService;
import eu.esdihumboldt.hale.common.core.io.project.ProjectInfo;
import eu.esdihumboldt.hale.common.core.io.project.ProjectInfoService;
import eu.esdihumboldt.hale.common.core.io.project.ProjectReader;
import eu.esdihumboldt.hale.common.core.io.project.model.ExportConfigurationMap;
import eu.esdihumboldt.hale.common.core.io.project.model.IOConfiguration;
import eu.esdihumboldt.hale.common.core.io.project.model.Project;
import eu.esdihumboldt.hale.common.core.io.supplier.LocatableInputSupplier;
import eu.esdihumboldt.hale.common.core.report.ReportHandler;
import eu.esdihumboldt.hale.common.core.service.ServiceManager;
import eu.esdihumboldt.hale.common.core.service.ServiceProvider;
import eu.esdihumboldt.hale.common.headless.HeadlessIO;
import eu.esdihumboldt.hale.common.headless.TransformationEnvironment;
import eu.esdihumboldt.hale.common.instance.io.InstanceIO;
import eu.esdihumboldt.hale.common.instance.io.InstanceWriter;
import eu.esdihumboldt.hale.common.schema.SchemaSpaceID;
import eu.esdihumboldt.hale.common.schema.model.SchemaSpace;
/**
* Transformation environment based on a {@link Project}.
*
* @author Simon Templer
*/
public class ProjectTransformationEnvironment implements TransformationEnvironment {
private static final ALogger log = ALoggerFactory
.getLogger(ProjectTransformationEnvironment.class);
private final Project project;
private final String id;
private final SchemaSpace sourceSchema;
private final SchemaSpace targetSchema;
private final Alignment alignment;
private final Map<String, IOConfiguration> exportTemplates = new ExportConfigurationMap();
private final Map<String, IOConfiguration> exportPresets = new ExportConfigurationMap();
private final Map<Class<?>, Object> customServices = new HashMap<>();
/**
* Project context service provider.
*/
private final ServiceProvider serviceProvider = new ServiceProvider() {
private final ServiceProvider projectScope = new ServiceManager(
ServiceManager.SCOPE_PROJECT);
@SuppressWarnings("unchecked")
@Override
public <T> T getService(Class<T> serviceInterface) {
if (customServices.containsKey(serviceInterface)) {
return (T) customServices.get(serviceInterface);
}
// FIXME global scope not supported yet
return projectScope.getService(serviceInterface);
}
};
/**
* Create a transformation environment based on a project file.
*
* @param id the identifier for the transformation environment
* @param input the project file input
* @param reportHandler the report handler for the reports during project
* loading, may be <code>null</code>
* @throws IOException if loading the project fails
*/
public ProjectTransformationEnvironment(String id,
LocatableInputSupplier<? extends InputStream> input, ReportHandler reportHandler)
throws IOException {
this(id, input, reportHandler, null);
}
/**
* Create a transformation environment based on a project file.
*
* @param id the identifier for the transformation environment
* @param input the project file input
* @param reportHandler the report handler for the reports during project
* loading, may be <code>null</code>
* @param additionalAdvisors a map with additional I/O advisors, action ID
* mapped to advisor, may be <code>null</code>
* @throws IOException if loading the project fails
*/
public ProjectTransformationEnvironment(String id,
LocatableInputSupplier<? extends InputStream> input, ReportHandler reportHandler,
Map<String, IOAdvisor<?>> additionalAdvisors) throws IOException {
super();
this.id = id;
// load the project
URI location = input.getLocation();
ProjectReader reader = HaleIO.findIOProvider(ProjectReader.class, input,
(location != null) ? (location.getPath()) : (null));
if (reader != null) {
// configure reader
reader.setSource(input);
HeadlessProjectAdvisor advisor = new HeadlessProjectAdvisor(reportHandler,
serviceProvider, additionalAdvisors);
HeadlessIO.executeProvider(reader, advisor, null, reportHandler);
// XXX progress???!!
project = advisor.getProject();
sourceSchema = advisor.getSourceSchema();
targetSchema = advisor.getTargetSchema();
alignment = advisor.getAlignment();
addService(FunctionService.class, new AlignmentFunctionService(alignment));
addService(TransformationFunctionService.class,
new AlignmentTransformationFunctionService(alignment));
// make TransformationSchemas service available
addService(TransformationSchemas.class, new TransformationSchemas() {
@Override
public SchemaSpace getSchemas(SchemaSpaceID spaceID) {
switch (spaceID) {
case SOURCE:
return sourceSchema;
case TARGET:
return targetSchema;
default:
return null;
}
}
});
// make project information available
addService(ProjectInfoService.class, new FixedProjectInfoService(project, location));
// make code lists available
addService(CodeListRegistry.class, advisor.getCodeListRegistry());
init(project);
}
else {
throw new IOException("Cannot load project, no corresponding I/O provider found.");
}
}
/**
* Add a custom service to the transformation environment.
*
* @param serviceInterface the service interface
* @param service the service
*/
public <T> void addService(Class<T> serviceInterface, T service) {
customServices.put(serviceInterface, service);
}
/**
* Initialize the environment based on the loaded project.
*
* @param project the project
*/
protected void init(Project project) {
// TODO import/export configurations for data
// export presets
for (Entry<String, IOConfiguration> preset : project.getExportConfigurations().entrySet()) {
IOConfiguration conf = preset.getValue();
if (InstanceIO.ACTION_SAVE_TRANSFORMED_DATA.equals(conf.getActionId())) {
// configuration for data export
IOConfiguration c = conf.clone();
// check provider
IOProviderDescriptor factory = HaleIO.findIOProviderFactory(InstanceWriter.class,
null, c.getProviderId());
if (factory != null) {
String name = preset.getKey();
if (Strings.isNullOrEmpty(name)) {
name = factory.getDisplayName();
}
exportPresets.put(name, c);
}
else {
log.error("I/O provider for export preset not found.");
}
}
}
// export templates
Collection<IOProviderDescriptor> writerFactories = HaleIO
.getProviderFactories(InstanceWriter.class);
for (IOProviderDescriptor factory : writerFactories) {
try {
InstanceWriter writer = (InstanceWriter) factory.createExtensionObject();
writer.setTargetSchema(getTargetSchema());
writer.checkCompatibility();
IOConfiguration conf = new IOConfiguration();
conf.setActionId(InstanceIO.ACTION_SAVE_TRANSFORMED_DATA);
conf.setProviderId(factory.getIdentifier());
exportTemplates.put(factory.getDisplayName(), conf);
} catch (IOProviderConfigurationException e) {
// ignore
} catch (Exception e) {
log.error("Error initializing instance writer for testing compatibility", e);
}
}
}
@Override
public Map<String, ? extends IOConfiguration> getExportPresets() {
return Collections.unmodifiableMap(exportPresets);
}
@Override
public Map<String, ? extends IOConfiguration> getExportTemplates() {
return Collections.unmodifiableMap(exportTemplates);
}
@Override
public String getId() {
return id;
}
@Override
public ProjectInfo getProjectInfo() {
return project;
}
/**
* Get the associated project.
*
* @return the project, must not be changed
*/
public Project getProject() {
return project;
}
@Override
public Alignment getAlignment() {
return alignment;
}
@Override
public SchemaSpace getSourceSchema() {
return sourceSchema;
}
@Override
public SchemaSpace getTargetSchema() {
return targetSchema;
}
@Override
public <T> T getService(Class<T> serviceInterface) {
return serviceProvider.getService(serviceInterface);
}
}