/*
* 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.server.webtransform.war.pages;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.extensions.markup.html.form.select.IOptionRenderer;
import org.apache.wicket.extensions.markup.html.form.select.Select;
import org.apache.wicket.extensions.markup.html.form.select.SelectOptions;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.IValidator;
import org.apache.wicket.validation.ValidationError;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import de.fhg.igd.slf4jplus.ALogger;
import de.fhg.igd.slf4jplus.ALoggerFactory;
import eu.esdihumboldt.hale.common.core.io.ExportProvider;
import eu.esdihumboldt.hale.common.core.io.HaleIO;
import eu.esdihumboldt.hale.common.core.io.IOProvider;
import eu.esdihumboldt.hale.common.core.io.Value;
import eu.esdihumboldt.hale.common.core.io.extension.IOProviderDescriptor;
import eu.esdihumboldt.hale.common.core.io.project.model.IOConfiguration;
import eu.esdihumboldt.hale.common.core.io.supplier.FileIOSupplier;
import eu.esdihumboldt.hale.common.core.io.supplier.LocatableInputSupplier;
import eu.esdihumboldt.hale.common.headless.EnvironmentService;
import eu.esdihumboldt.hale.common.headless.HeadlessIO;
import eu.esdihumboldt.hale.common.headless.TransformationEnvironment;
import eu.esdihumboldt.hale.common.headless.transform.TransformationWorkspace;
import eu.esdihumboldt.hale.common.instance.io.InstanceReader;
import eu.esdihumboldt.hale.common.instance.io.InstanceWriter;
/**
* Form for uploading and transforming data.
*
* @author Simon Templer
*/
public class UploadAndTransForm extends Form<Void> {
/**
* Model for a parameter field.
*/
private class FieldFieldModel implements IModel<String> {
private static final long serialVersionUID = 7866817725347689084L;
private final String param;
/**
* Create a model for a parameter field.
*
* @param param the parameter name
*/
public FieldFieldModel(String param) {
this.param = param;
}
@Override
public void detach() {
// nuthin'
}
@Override
public String getObject() {
Value value = getTarget().getConfig().getProviderConfiguration().get(param);
if (value != null) {
return value.as(String.class);
}
return null;
}
@Override
public void setObject(String object) {
getTarget().getConfig().getProviderConfiguration().put(param, Value.of(object));
}
}
/**
* Choice renderer for content type IDs.
*/
private static class ContentTypeChoiceRenderer implements IChoiceRenderer<String> {
private static final long serialVersionUID = -6542046435287452517L;
@Override
public Object getDisplayValue(String object) {
return Platform.getContentTypeManager().getContentType(object).getName();
}
@Override
public String getIdValue(String object, int index) {
return object;
}
}
private static final long serialVersionUID = 8904573677189598470L;
private static final ALogger log = ALoggerFactory.getLogger(UploadAndTransForm.class);
@SpringBean
private EnvironmentService environmentService;
private final String projectId;
private final FileUploadField file;
private NamedIOConfiguration target;
private static final IOptionRenderer<NamedIOConfiguration> RENDERER = new IOptionRenderer<NamedIOConfiguration>() {
private static final long serialVersionUID = 4714894437575668850L;
@Override
public String getDisplayValue(NamedIOConfiguration object) {
String name = object.getName();
if (name != null && !name.isEmpty()) {
return name;
}
return object.getConfig().getProviderId();
}
@Override
public IModel<NamedIOConfiguration> getModel(NamedIOConfiguration value) {
return new Model<NamedIOConfiguration>(value);
}
};
/**
* Create a form for uploading and transforming data.
*
* @param id the component ID
* @param projectId the project ID
*/
public UploadAndTransForm(String id, String projectId) {
super(id);
this.projectId = projectId;
// multi-part always needed for uploads
setMultiPart(true);
add(new FeedbackPanel("feedback"));
// Add file input field
add(file = new FileUploadField("upload"));
file.add(new IValidator<List<FileUpload>>() {
private static final long serialVersionUID = -5668788086384105101L;
@Override
public void validate(IValidatable<List<FileUpload>> validatable) {
if (validatable.getValue().isEmpty()) {
validatable.error(new ValidationError("No source files specified."));
}
}
});
// target selection
Select<NamedIOConfiguration> selectTarget = new Select<NamedIOConfiguration>("target",
new PropertyModel<NamedIOConfiguration>(this, "target"));
add(selectTarget);
TransformationEnvironment env = environmentService.getEnvironment(projectId);
// determine presets
Collection<NamedIOConfiguration> presetList = new ArrayList<>();
for (Entry<String, ? extends IOConfiguration> entry : env.getExportPresets().entrySet()) {
presetList.add(new NamedIOConfiguration(entry.getKey(), entry.getValue()));
}
SelectOptions<NamedIOConfiguration> presets = new SelectOptions<NamedIOConfiguration>(
"presets", presetList, RENDERER);
selectTarget.add(presets);
// determine valid exporters
Collection<NamedIOConfiguration> templateList = new ArrayList<>();
for (Entry<String, ? extends IOConfiguration> entry : env.getExportTemplates().entrySet()) {
presetList.add(new NamedIOConfiguration(entry.getKey(), entry.getValue()));
}
SelectOptions<NamedIOConfiguration> exporters = new SelectOptions<NamedIOConfiguration>(
"exporters", templateList, RENDERER);
selectTarget.add(exporters);
// initial selection
if (!presetList.isEmpty()) {
setTarget(presetList.iterator().next());
}
else if (!templateList.isEmpty()) {
setTarget(templateList.iterator().next());
}
// panel for I/O configuration
final WebMarkupContainer config = new WebMarkupContainer("config");
config.setOutputMarkupId(true);
add(config);
IModel<List<String>> parameterModel = new LoadableDetachableModel<List<String>>() {
private static final long serialVersionUID = 1018038661733512580L;
@Override
protected List<String> load() {
Set<String> properties = new LinkedHashSet<String>();
if (target != null && target.getConfig().getProviderId() != null) {
// must have
properties.add(IOProvider.PARAM_CONTENT_TYPE);
// what is supported
IOProvider p = HeadlessIO.loadProvider(target.getConfig());
properties.addAll(p.getSupportedParameters());
// not allowed
properties.remove(ExportProvider.PARAM_TARGET);
}
return new ArrayList<String>(properties);
}
};
ListView<String> parameterView = new ListView<String>("param", parameterModel) {
private static final long serialVersionUID = -7838477347365823022L;
@Override
protected void populateItem(ListItem<String> item) {
boolean isContentType = IOProvider.PARAM_CONTENT_TYPE.equals(item.getModelObject());
// name
item.add(new Label("name", item.getModelObject()));
// text field
TextField<String> textField = new TextField<String>("field", new FieldFieldModel(
item.getModelObject()));
textField.setVisible(!isContentType);
item.add(textField);
// contentType select field
DropDownChoice<String> contentType;
if (isContentType) {
IOProviderDescriptor pf = HaleIO.findIOProviderFactory(InstanceWriter.class,
null, getTarget().getConfig().getProviderId());
List<String> types = new ArrayList<String>();
for (IContentType type : pf.getSupportedTypes()) {
types.add(type.getId());
}
contentType = new DropDownChoice<String>("contentType", new FieldFieldModel(
item.getModelObject()), types, new ContentTypeChoiceRenderer());
}
else {
contentType = new DropDownChoice<String>("contentType", new ArrayList<String>());
contentType.setVisible(false);
}
item.add(contentType);
}
};
config.add(parameterView);
// update parameter panel on target selection change
selectTarget.add(new AjaxFormComponentUpdatingBehavior("onchange") {
private static final long serialVersionUID = 8004015871380712045L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
// update config panel
target.add(config);
}
});
}
/**
* @see Form#onSubmit()
*/
@Override
protected void onSubmit() {
List<FileUpload> uploads = file.getFileUploads();
if (uploads == null || uploads.isEmpty()) {
error("Please specify files to transform.");
return;
}
final TransformationWorkspace workspace = new TransformationWorkspace();
List<InstanceReader> readers = Lists.transform(uploads,
new Function<FileUpload, InstanceReader>() {
private int count;
@Override
public InstanceReader apply(@Nullable final FileUpload input) {
/*
* Copy uploaded file to source folder, because the
* input stream retrieved from the FileUpload is
* automatically closed with the end of the request.
*/
File file = new File(workspace.getSourceFolder(), (count++) + "_"
+ input.getClientFileName());
try {
input.writeTo(file);
} catch (IOException e) {
throw new IllegalStateException("Unable to read uploaded source file",
e);
}
InstanceReader reader = null;
try {
LocatableInputSupplier<? extends InputStream> in = new FileIOSupplier(
file);
reader = HaleIO.findIOProvider(InstanceReader.class, in,
input.getClientFileName());
if (reader != null) {
reader.setSource(in);
}
} catch (Exception e) {
throw new IllegalStateException("Unable to read uploaded source file",
e);
}
if (reader == null) {
throw new IllegalStateException(
"Could not find I/O provider for source file.");
}
return reader;
}
});
try {
workspace.transform(projectId, readers, target.getConfig());
setResponsePage(StatusPage.class,
new PageParameters().add(StatusPage.PARAMETER_WORKSPACE, workspace.getId()));
} catch (Exception e) {
log.error("Error launching transformation process", e);
error("Error launching transformation process");
workspace.delete();
}
}
/**
* @return the target
*/
public NamedIOConfiguration getTarget() {
return target;
}
/**
* @param target the target to set
*/
public void setTarget(NamedIOConfiguration target) {
this.target = target;
}
}