/* * Copyright 2017 * Ubiquitous Knowledge Processing (UKP) Lab and FG Language Technology * Technische Universität Darmstadt * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.tudarmstadt.ukp.clarin.webanno.ui.tagsets; import static de.tudarmstadt.ukp.clarin.webanno.export.model.ExportedTagSetConstant.JSON_FORMAT; import static de.tudarmstadt.ukp.clarin.webanno.export.model.ExportedTagSetConstant.TAB_FORMAT; import static java.util.Arrays.asList; import static java.util.Objects.isNull; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior.onUpdateChoice; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.form.CheckBox; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextArea; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.util.resource.FileResourceStream; import org.apache.wicket.validation.IValidatable; import org.apache.wicket.validation.IValidator; import org.apache.wicket.validation.ValidationError; import de.agilecoders.wicket.core.markup.html.bootstrap.form.BootstrapRadioChoice; import de.tudarmstadt.ukp.clarin.webanno.api.AnnotationSchemaService; import de.tudarmstadt.ukp.clarin.webanno.export.model.ExportedTag; import de.tudarmstadt.ukp.clarin.webanno.export.model.ExportedTagSet; import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature; import de.tudarmstadt.ukp.clarin.webanno.model.Project; import de.tudarmstadt.ukp.clarin.webanno.model.Tag; import de.tudarmstadt.ukp.clarin.webanno.model.TagSet; import de.tudarmstadt.ukp.clarin.webanno.support.JSONUtil; import de.tudarmstadt.ukp.clarin.webanno.support.dialog.ConfirmationDialog; import de.tudarmstadt.ukp.clarin.webanno.support.lambda.LambdaAjaxButton; import de.tudarmstadt.ukp.clarin.webanno.support.lambda.LambdaAjaxLink; import de.tudarmstadt.ukp.clarin.webanno.support.lambda.LambdaPanel; import de.tudarmstadt.ukp.clarin.webanno.support.wicket.AjaxDownloadLink; public class TagSetEditorPanel extends LambdaPanel { private static final long serialVersionUID = 3084260865116114184L; private @SpringBean AnnotationSchemaService annotationSchemaService; private ConfirmationDialog confirmationDialog; private IModel<Project> selectedProject; private IModel<TagSet> selectedTagSet; private IModel<Tag> selectedTag; private IModel<String> exportFormat; public TagSetEditorPanel(String aId, IModel<Project> aProject, IModel<TagSet> aTagSet, IModel<Tag> aSelectedTag) { super(aId, aTagSet); setOutputMarkupId(true); setOutputMarkupPlaceholderTag(true); selectedProject = aProject; selectedTagSet = aTagSet; selectedTag = aSelectedTag; exportFormat = Model.of(supportedFormats().get(0)); Form<TagSet> form = new Form<>("form", CompoundPropertyModel.of(aTagSet)); add(form); form.add(new TextField<String>("name") .add(new TagSetExistsValidator()) .setRequired(true)); form.add(new TextField<String>("language")); form.add(new TextArea<String>("description")); form.add(new CheckBox("createTag")); form.add(new LambdaAjaxButton<>("save", this::actionSave)); form.add(new LambdaAjaxLink("delete", this::actionDelete) .onConfigure(_this -> _this.setVisible(form.getModelObject().getId() != null))); form.add(new LambdaAjaxLink("cancel", this::actionCancel)); BootstrapRadioChoice<String> format = new BootstrapRadioChoice<>("format", exportFormat, LoadableDetachableModel.of(this::supportedFormats)); // The AjaxDownloadLink does not submit the form, so the format radio-buttons need to // submit themselves so their value is available when the export button is pressed format.add(onUpdateChoice(_target -> { /*NO-OP*/ })); form.add(format); form.add(new AjaxDownloadLink("export", LoadableDetachableModel.of(this::export))); confirmationDialog = new ConfirmationDialog("confirmationDialog"); confirmationDialog.setTitleModel(new StringResourceModel("DeleteDialog.title", this)); add(confirmationDialog); } private List<String> supportedFormats() { return asList(JSON_FORMAT, TAB_FORMAT); } private void actionSave(AjaxRequestTarget aTarget, Form<Tag> aForm) { if (isNull(selectedTagSet.getObject().getId())) { if (annotationSchemaService.existsTagSet(selectedTagSet.getObject() .getName(), selectedProject.getObject())) { error("Only one tagset per project is allowed!"); } } selectedTagSet.getObject().setProject(selectedProject.getObject()); annotationSchemaService.createTagSet(selectedTagSet.getObject()); // Reload whole page because master panel also needs to be reloaded. aTarget.add(getPage()); } private void actionDelete(AjaxRequestTarget aTarget) { confirmationDialog.setContentModel(new StringResourceModel("DeleteDialog.text", this) .setParameters(selectedTagSet.getObject().getName())); confirmationDialog.show(aTarget); confirmationDialog.setConfirmAction((_target) -> { // If the tagset is used in any features, clear the tagset on these features when // the tagset is deleted! for (AnnotationFeature ft : annotationSchemaService .listAnnotationFeature(selectedProject.getObject())) { if (ft.getTagset() != null && ft.getTagset().equals(selectedTagSet.getObject())) { ft.setTagset(null); annotationSchemaService.createFeature(ft); } } annotationSchemaService.removeTagSet(selectedTagSet.getObject()); _target.add(getPage()); actionCancel(_target); }); } private void actionCancel(AjaxRequestTarget aTarget) { selectedTagSet.setObject(null); selectedTag.setObject(null); // Reload whole page because master panel also needs to be reloaded. aTarget.add(getPage()); } private FileResourceStream export() { File exportFile = null; if (exportFormat.getObject().equals(JSON_FORMAT)) { try { exportFile = File.createTempFile("exportedtagsets", ".json"); } catch (IOException e1) { error("Unable to create temporary File!!"); return null; } if (isNull(selectedTagSet.getObject().getId())) { error("Project not yet created. Please save project details first!"); } else { TagSet tagSet = selectedTagSet.getObject(); ExportedTagSet exTagSet = new ExportedTagSet(); exTagSet.setDescription(tagSet.getDescription()); exTagSet.setLanguage(tagSet.getLanguage()); exTagSet.setName(tagSet.getName()); List<ExportedTag> exportedTags = new ArrayList<>(); for (Tag tag : annotationSchemaService.listTags(tagSet)) { ExportedTag exportedTag = new ExportedTag(); exportedTag.setDescription(tag.getDescription()); exportedTag.setName(tag.getName()); exportedTags.add(exportedTag); } exTagSet.setTags(exportedTags); try { JSONUtil.generatePrettyJson(exTagSet, exportFile); } catch (IOException e) { error("File Path not found or No permision to save the file!"); } info("TagSets successfully exported to :" + exportFile.getAbsolutePath()); } } else if (exportFormat.getObject().equals(TAB_FORMAT)) { TagSet tagSet = selectedTagSet.getObject(); try { exportFile = File.createTempFile("exportedtagsets", ".txt"); } catch (IOException e1) { error("Unable to create temporary File!!"); } OutputStream os; OutputStreamWriter osw; BufferedWriter bw; try { String tagSetDescription = tagSet.getDescription() == null ? "" : tagSet.getDescription(); os = new FileOutputStream(exportFile); osw = new OutputStreamWriter(os, "UTF-8"); bw = new BufferedWriter(osw); bw.write(tagSet.getName() + "\t" + tagSetDescription.replace("\n", "\\n") + "\n"); bw.write(tagSet.getLanguage() + "\t" + " \n"); for (Tag tag : annotationSchemaService.listTags(tagSet)) { String tagDescription = tag.getDescription() == null ? "" : tag .getDescription(); bw.write(tag.getName() + "\t" + tagDescription.replace("\n", "\\n") + "\n"); } bw.flush(); bw.close(); } catch (FileNotFoundException e) { error("The file for export not found " + ExceptionUtils.getRootCauseMessage(e)); } catch (UnsupportedEncodingException e) { error("Unsupported encoding " + ExceptionUtils.getRootCauseMessage(e)); } catch (IOException e) { error(ExceptionUtils.getRootCause(e)); } } return new FileResourceStream(exportFile); } private class TagSetExistsValidator implements IValidator<String> { private static final long serialVersionUID = -5775158293783334223L; @Override public void validate(IValidatable<String> aValidatable) { String newName = aValidatable.getValue(); String oldName = aValidatable.getModel().getObject(); if (!StringUtils.equals(newName, oldName) && isNotBlank(newName) && annotationSchemaService.existsTagSet(newName, selectedProject.getObject())) { aValidatable.error(new ValidationError( "Another tag set with the same name exists. Please try a different name")); } } } }