/* * Copyright 2012 * 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.project; import static org.apache.commons.collections.CollectionUtils.isEmpty; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.wicket.AttributeModifier; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.OnChangeAjaxBehavior; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.CheckBox; import org.apache.wicket.markup.html.form.ChoiceRenderer; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.ListChoice; import org.apache.wicket.markup.html.form.TextArea; 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.link.DownloadLink; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.context.SecurityContextHolder; import de.tudarmstadt.ukp.clarin.webanno.api.AnnotationSchemaService; import de.tudarmstadt.ukp.clarin.webanno.api.dao.JsonImportUtil; import de.tudarmstadt.ukp.clarin.webanno.export.model.ExportedTagSetConstant; import de.tudarmstadt.ukp.clarin.webanno.export.model.TagSet; 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.security.UserDao; import de.tudarmstadt.ukp.clarin.webanno.security.model.User; import de.tudarmstadt.ukp.clarin.webanno.support.EntityModel; import de.tudarmstadt.ukp.clarin.webanno.support.JSONUtil; import de.tudarmstadt.ukp.clarin.webanno.ui.core.settings.ProjectSettingsPanel; import de.tudarmstadt.ukp.clarin.webanno.ui.core.settings.ProjectSettingsPanelBase; /** * A Panel user to manage Tagsets. */ @ProjectSettingsPanel(label="Tagsets", prio=400) public class ProjectTagSetsPanel extends ProjectSettingsPanelBase { private static final long serialVersionUID = 7004037105647505760L; private static final Logger LOG = LoggerFactory.getLogger(ProjectTagSetsPanel.class); private @SpringBean AnnotationSchemaService annotationService; private @SpringBean UserDao userRepository; private List<FileUpload> uploadedFiles; private FileUploadField fileUpload; DropDownChoice<String> importTagsetFormat; DropDownChoice<String> exportTagsetFormat; private String selectedExporTagsetFormat = ExportedTagSetConstant.JSON_FORMAT; private final TagSetSelectionForm tagSetSelectionForm; private final TagSelectionForm tagSelectionForm; private TagSetDetailForm tagSetDetailForm; private final TagDetailForm tagDetailForm; private final ImportTagSetForm importTagSetForm; public ProjectTagSetsPanel(String id, final IModel<Project> aProjectModel) { super(id, aProjectModel); tagSetSelectionForm = new TagSetSelectionForm("tagSetSelectionForm"); tagSelectionForm = new TagSelectionForm("tagSelectionForm"); tagSelectionForm.setVisible(false); tagSetDetailForm = new TagSetDetailForm("tagSetDetailForm"); tagSetDetailForm.setVisible(false); tagDetailForm = new TagDetailForm("tagDetailForm"); tagDetailForm.setVisible(false); importTagSetForm = new ImportTagSetForm("importTagSetForm"); add(tagSetSelectionForm); add(tagSelectionForm); add(tagSetDetailForm); add(tagDetailForm); add(importTagSetForm); } private class TagSetSelectionForm extends Form<SelectionModel> { private static final long serialVersionUID = -1L; public TagSetSelectionForm(String id) { super(id, new CompoundPropertyModel<SelectionModel>(new SelectionModel())); add(new Button("create", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; @Override public void onSubmit() { if (ProjectTagSetsPanel.this.getModelObject().getId() == 0) { error("Project not yet created. Please save project details first!"); } else { TagSetSelectionForm.this.getModelObject().tagSet = null; tagSetDetailForm .setModelObject(new de.tudarmstadt.ukp.clarin.webanno.model.TagSet()); tagSetDetailForm.setVisible(true); tagDetailForm.setVisible(false); tagSelectionForm.setVisible(false); } } }); add(new ListChoice<de.tudarmstadt.ukp.clarin.webanno.model.TagSet>("tagSet") { private static final long serialVersionUID = 1L; { setChoices(new LoadableDetachableModel<List<de.tudarmstadt.ukp.clarin.webanno.model.TagSet>>() { private static final long serialVersionUID = 1L; @Override protected List<de.tudarmstadt.ukp.clarin.webanno.model.TagSet> load() { Project project = ProjectTagSetsPanel.this.getModelObject(); if (project.getId() != 0) { return annotationService.listTagSets(project); } else { return new ArrayList<de.tudarmstadt.ukp.clarin.webanno.model.TagSet>(); } } }); setChoiceRenderer(new ChoiceRenderer<de.tudarmstadt.ukp.clarin.webanno.model.TagSet>() { private static final long serialVersionUID = -2000622431037285685L; @Override public Object getDisplayValue( de.tudarmstadt.ukp.clarin.webanno.model.TagSet aObject) { return aObject.getName(); } }); setNullValid(false); } @Override protected void onSelectionChanged( de.tudarmstadt.ukp.clarin.webanno.model.TagSet aNewSelection) { if (aNewSelection != null) { tagSetDetailForm.clearInput(); tagSetDetailForm.setModelObject(aNewSelection); tagSetDetailForm.setVisible(true); tagSelectionForm.setVisible(true); tagDetailForm.setModelObject(new Tag()); tagDetailForm.setVisible(true); TagSetSelectionForm.this.setVisible(true); } } @Override protected boolean wantOnSelectionChangedNotifications() { return true; } @Override protected CharSequence getDefaultChoice(String aSelectedValue) { return ""; } }).setOutputMarkupId(true); } } private class ImportTagSetForm extends Form<String> { private static final long serialVersionUID = 5286655225171641733L; private CheckBox overwriteTagsetFlag; @SuppressWarnings({ "unchecked", "rawtypes" }) public ImportTagSetForm(String id) { super(id); add(fileUpload = new FileUploadField("content", new Model())); add(importTagsetFormat = new DropDownChoice<String>("importTagsetFormat", new Model<String>(selectedExporTagsetFormat), Arrays.asList(new String[] { ExportedTagSetConstant.JSON_FORMAT, ExportedTagSetConstant.TAB_FORMAT }))); overwriteTagsetFlag = new CheckBox("overwriteTagset", Model.of(Boolean.FALSE)); add(overwriteTagsetFlag); add(new Button("import", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; @Override public void onSubmit() { uploadedFiles = fileUpload.getFileUploads(); Project project = ProjectTagSetsPanel.this.getModelObject(); String username = SecurityContextHolder.getContext().getAuthentication() .getName(); User user = userRepository.get(username); if (isEmpty(uploadedFiles)) { error("Please choose file with tagset before uploading"); return; } else if (project.getId() == 0) { error("Project not yet created, please save project Details!"); return; } if (importTagsetFormat.getModelObject().equals( ExportedTagSetConstant.JSON_FORMAT)) { for (FileUpload tagFile : uploadedFiles) { InputStream tagInputStream; try { tagInputStream = tagFile.getInputStream(); if(overwriteTagsetFlag.getModelObject()){ JsonImportUtil.importTagSetFromJsonWithOverwrite(project, tagInputStream, annotationService); } else { JsonImportUtil.importTagSetFromJson(project, tagInputStream, annotationService); } } catch (IOException e) { error("Error Importing TagSet " + ExceptionUtils.getRootCauseMessage(e)); } } } else if (importTagsetFormat.getModelObject().equals( ExportedTagSetConstant.TAB_FORMAT)) { for (FileUpload tagFile : uploadedFiles) { InputStream tagInputStream; try { tagInputStream = tagFile.getInputStream(); String text = IOUtils.toString(tagInputStream, "UTF-8"); Map<String, String> tabbedTagsetFromFile = ImportUtil .getTagSetFromFile(text); Set<String> listOfTagsFromFile = tabbedTagsetFromFile.keySet(); int i = 0; String tagSetName = ""; String tagSetDescription = ""; String tagsetLanguage = ""; de.tudarmstadt.ukp.clarin.webanno.model.TagSet tagSet = null; for (String key : listOfTagsFromFile) { // the first key is the tagset name and its // description if (i == 0) { tagSetName = key; tagSetDescription = tabbedTagsetFromFile.get(key); } // the second key is the tagset language else if (i == 1) { tagsetLanguage = key; // remove and replace the tagset if it // exist if (annotationService.existsTagSet(tagSetName, project)) { // If overwrite is enabled if (overwriteTagsetFlag.getModelObject()) { tagSet = annotationService.getTagSet(tagSetName, project); annotationService.removeAllTags(tagSet); } else { tagSet = new de.tudarmstadt.ukp.clarin.webanno.model.TagSet(); tagSet.setName(JsonImportUtil.copyTagSetName( annotationService, tagSetName, project)); } } else { tagSet = new de.tudarmstadt.ukp.clarin.webanno.model.TagSet(); tagSet.setName(tagSetName); } tagSet.setDescription(tagSetDescription .replace("\\n", "\n")); tagSet.setLanguage(tagsetLanguage); tagSet.setProject(project); annotationService.createTagSet(tagSet); } // otherwise it is a tag entry, add the tag // to the tagset else { Tag tag = new Tag(); tag.setDescription(tabbedTagsetFromFile.get(key).replace( "\\n", "\n")); tag.setName(key); tag.setTagSet(tagSet); annotationService.createTag(tag); } i++; } } catch (Exception e) { error("Error importing tag set: " + ExceptionUtils.getRootCauseMessage(e)); LOG.error("Error importing tag set", e); } } } tagSetSelectionForm.setModelObject(new SelectionModel()); tagSelectionForm.setVisible(false); tagDetailForm.setVisible(false); } }); } } public class SelectionModel implements Serializable { private static final long serialVersionUID = -1L; @SuppressWarnings("unused") private de.tudarmstadt.ukp.clarin.webanno.model.TagSet tagSet; private Tag tag; } private class TagSetDetailForm extends Form<de.tudarmstadt.ukp.clarin.webanno.model.TagSet> { private static final long serialVersionUID = -1L; public TagSetDetailForm(String id) { super(id, new CompoundPropertyModel<de.tudarmstadt.ukp.clarin.webanno.model.TagSet>( new EntityModel<de.tudarmstadt.ukp.clarin.webanno.model.TagSet>( new de.tudarmstadt.ukp.clarin.webanno.model.TagSet()))); final Project project = ProjectTagSetsPanel.this.getModelObject(); TextField<String> tagSetName = new TextField<String>("name"); tagSetName.setRequired(true); add(tagSetName); add(new TextArea<String>("description").setOutputMarkupPlaceholderTag(true)); add(new TextField<String>("language")); add(new CheckBox("createTag")); add(new Button("save", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; @Override public void validate() { super.validate(); //Check if name is already used if(annotationService.existsTagSet(tagSetName.getInput(), project) && !tagSetName.getInput().equals(tagSetName.getModelObject())){ error("Only one tagset per project is allowed!"); } } @Override public void onSubmit() { de.tudarmstadt.ukp.clarin.webanno.model.TagSet tagSet = TagSetDetailForm.this .getModelObject(); if (tagSet.getId() == 0) { if (annotationService.existsTagSet(TagSetDetailForm.this.getModelObject() .getName(), project)) { error("Only one tagset per project is allowed!"); } else { String username = SecurityContextHolder.getContext() .getAuthentication().getName(); User user = userRepository.get(username); tagSet.setProject(ProjectTagSetsPanel.this.getModelObject()); try { annotationService.createTagSet(tagSet); tagSelectionForm.setVisible(true); tagDetailForm.setVisible(true); // annotationService.createType(tagSet.getFeature().getLayer(), // user); } catch (IOException e) { error("unable to create Logger file while creating the TagSet" + ":" + ExceptionUtils.getRootCauseMessage(e)); } TagSetDetailForm.this.setModelObject(tagSet); } } } }); Button removeTagSetButton =new Button("remove", new StringResourceModel("label")) { private static final long serialVersionUID = -3794689234509984031L; @Override public void onSubmit() { de.tudarmstadt.ukp.clarin.webanno.model.TagSet tagSet = TagSetDetailForm.this .getModelObject(); if (tagSet.getId() != 0) { for (AnnotationFeature ft : annotationService.listAnnotationFeature(tagSet .getProject())) { if (ft.getTagset() != null && ft.getTagset().equals(tagSet)) { ft.setTagset(null); annotationService.createFeature(ft); } } annotationService.removeTagSet(tagSet); TagSetDetailForm.this.setModelObject(null); tagSelectionForm.setVisible(false); tagDetailForm.setVisible(false); TagSetDetailForm.this.setVisible(false); } TagSetDetailForm.this .setModelObject(new de.tudarmstadt.ukp.clarin.webanno.model.TagSet()); } }; // Add check to prevent accidental delete operation removeTagSetButton.add(new AttributeModifier("onclick", "if(!confirm('Do you really want to delete this Tagset?')) return false;")); add(removeTagSetButton); // add(new Button("remove", new ResourceModel("label")) // { // private static final long serialVersionUID = 1L; // // @Override // public void onSubmit() // { // de.tudarmstadt.ukp.clarin.webanno.model.TagSet tagSet = TagSetDetailForm.this // .getModelObject(); // if (tagSet.getId() != 0) { // for (AnnotationFeature ft : annotationService.listAnnotationFeature(tagSet // .getProject())) { // if (ft.getTagset() != null && ft.getTagset().equals(tagSet)) { // ft.setTagset(null); // annotationService.createFeature(ft); // } // } // annotationService.removeTagSet(tagSet); // TagSetDetailForm.this.setModelObject(null); // tagSelectionForm.setVisible(false); // tagDetailForm.setVisible(false); // TagSetDetailForm.this.setVisible(false); // } // TagSetDetailForm.this // .setModelObject(new de.tudarmstadt.ukp.clarin.webanno.model.TagSet()); // } // }); add(new Button("cancel", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; { // Avoid saving data setDefaultFormProcessing(false); setVisible(true); } @Override public void onSubmit() { tagSetDetailForm.setModelObject(new de.tudarmstadt.ukp.clarin.webanno.model.TagSet()); // tagSetDetailForm.setModelObject(new de.tudarmstadt.ukp.clarin.webanno.model.TagSet()); // tagSetDetailForm.setVisible(false); // tagSelectionForm.setVisible(false); // tagDetailForm.setVisible(false); } }); add(exportTagsetFormat = new DropDownChoice<String>("exportTagsetFormat", new Model<String>(selectedExporTagsetFormat), Arrays.asList(new String[] { ExportedTagSetConstant.JSON_FORMAT, ExportedTagSetConstant.TAB_FORMAT })) { private static final long serialVersionUID = -3149305683012829848L; @Override protected void onSelectionChanged(String newSelection) { selectedExporTagsetFormat = newSelection; } @Override protected boolean wantOnSelectionChangedNotifications() { return true; } }); add(new DownloadLink("export", new LoadableDetachableModel<File>() { private static final long serialVersionUID = 840863954694163375L; @Override protected File load() { File exportFile = null; if (selectedExporTagsetFormat.equals(ExportedTagSetConstant.JSON_FORMAT)) { try { exportFile = File.createTempFile("exportedtagsets", ".json"); } catch (IOException e1) { error("Unable to create temporary File!!"); } if (ProjectTagSetsPanel.this.getModelObject().getId() == 0) { error("Project not yet created. Please save project details first!"); } else { de.tudarmstadt.ukp.clarin.webanno.model.TagSet tagSet = tagSetDetailForm .getModelObject(); TagSet exTagSet = new TagSet(); exTagSet.setDescription(tagSet.getDescription()); exTagSet.setLanguage(tagSet.getLanguage()); exTagSet.setName(tagSet.getName()); List<de.tudarmstadt.ukp.clarin.webanno.export.model.Tag> exportedTags = new ArrayList<de.tudarmstadt.ukp.clarin.webanno.export.model.Tag>(); for (Tag tag : annotationService.listTags(tagSet)) { de.tudarmstadt.ukp.clarin.webanno.export.model.Tag exportedTag = new de.tudarmstadt.ukp.clarin.webanno.export.model.Tag(); 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 (selectedExporTagsetFormat.equals(ExportedTagSetConstant.TAB_FORMAT)) { de.tudarmstadt.ukp.clarin.webanno.model.TagSet tagSet = tagSetDetailForm .getModelObject(); 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 : annotationService.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 exportFile; } }).setDeleteAfterDownload(true).setOutputMarkupId(true)); } } private class TagDetailForm extends Form<Tag> { private static final long serialVersionUID = -1L; public TagDetailForm(String id) { super(id, new CompoundPropertyModel<Tag>(new EntityModel<Tag>(new Tag()))); add(new TextField<String>("name").setRequired(true)); add(new TextArea<String>("description").setOutputMarkupPlaceholderTag(true)); add(new Button("save", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; @Override public void onSubmit() { Tag tag = TagDetailForm.this.getModelObject(); if (tag.getName().equals("|")) { error("[|] is not allowed!"); return; } if (tag.getId() == 0) { tag.setTagSet(tagSetDetailForm.getModelObject()); if (annotationService.existsTag(tag.getName(), tagSetDetailForm.getModelObject())) { error("This tag is already added for this tagset!"); } else { String username = SecurityContextHolder.getContext() .getAuthentication().getName(); User user = userRepository.get(username); try { annotationService.createTag(tag); } catch (IOException e) { error("unable to create a log file while creating the Tag " + ":" + ExceptionUtils.getRootCauseMessage(e)); } tagDetailForm.setModelObject(new Tag()); } } } }); add(new Button("remove", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; @Override public void onSubmit() { Tag tag = TagDetailForm.this.getModelObject(); if (tag.getId() != 0) { tag.setTagSet(tagSetDetailForm.getModelObject()); annotationService.removeTag(tag); tagDetailForm.setModelObject(new Tag()); } else { TagDetailForm.this.setModelObject(new Tag()); } } }); add(new Button("cancel", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; { // Avoid saving data setDefaultFormProcessing(false); setVisible(true); } @Override public void onSubmit() { tagDetailForm.setModelObject(new Tag()); // tagDetailForm.setModelObject(null); // tagDetailForm.setVisible(false); } }); } } public class TagSelectionForm extends Form<SelectionModel> { private static final long serialVersionUID = -1L; @SuppressWarnings("unused") private Tag selectedTag; private ListChoice<Tag> tags; public TagSelectionForm(String id) { // super(id); super(id, new CompoundPropertyModel<SelectionModel>(new SelectionModel())); add(tags = new ListChoice<Tag>("tag") { private static final long serialVersionUID = 1L; { setChoices(new LoadableDetachableModel<List<Tag>>() { private static final long serialVersionUID = 1L; @Override protected List<Tag> load() { if (tagSetDetailForm.getModelObject().getId() == 0) { return Arrays.asList(new Tag()); } return annotationService.listTags(tagSetDetailForm.getModelObject()); } }); setChoiceRenderer(new ChoiceRenderer<Tag>() { private static final long serialVersionUID = 4696303692557735150L; @Override public Object getDisplayValue(Tag aObject) { return aObject.getName(); } }); setNullValid(false); } @Override protected CharSequence getDefaultChoice(String aSelectedValue) { return ""; } }); tags.add(new OnChangeAjaxBehavior() { private static final long serialVersionUID = 7492425689121761943L; @Override protected void onUpdate(AjaxRequestTarget aTarget) { if (getModelObject().tag != null) { tagDetailForm.setModelObject(getModelObject().tag); aTarget.add(tagDetailForm.setOutputMarkupId(true)); } } }).setOutputMarkupId(true); add(new Button("new", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; @Override public void onSubmit() { tagDetailForm.setDefaultModelObject(new Tag()); } }); } } }