/* * Copyright 2015 * 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.File; import java.io.IOException; import java.util.List; import org.apache.commons.io.input.BOMInputStream; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.wicket.AttributeModifier; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.ChoiceRenderer; 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.PropertyModel; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.spring.injection.annot.SpringBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.tudarmstadt.ukp.clarin.webanno.constraints.ConstraintsService; import de.tudarmstadt.ukp.clarin.webanno.constraints.grammar.ConstraintsGrammar; import de.tudarmstadt.ukp.clarin.webanno.constraints.grammar.ParseException; import de.tudarmstadt.ukp.clarin.webanno.model.ConstraintSet; import de.tudarmstadt.ukp.clarin.webanno.model.Project; import de.tudarmstadt.ukp.clarin.webanno.support.EntityModel; import de.tudarmstadt.ukp.clarin.webanno.ui.core.settings.ProjectSettingsPanel; import de.tudarmstadt.ukp.clarin.webanno.ui.core.settings.ProjectSettingsPanelBase; /** * A Panel used to add Project Constraints Rules in a selected {@link Project}. */ @ProjectSettingsPanel(label="Constraints", prio=500) public class ProjectConstraintsPanel extends ProjectSettingsPanelBase { private static final long serialVersionUID = 8910455936756021733L; private static final Logger LOG = LoggerFactory.getLogger(ProjectConstraintsPanel.class); private @SpringBean ConstraintsService constraintsService; private SelectionForm selectionForm; private DetailForm detailForm; private ImportForm importForm; public ProjectConstraintsPanel(String id, IModel<Project> aProjectModel) { super(id, aProjectModel); add(selectionForm = new SelectionForm("selectionForm")); add(detailForm = new DetailForm("detailForm")); add(importForm = new ImportForm("importForm")); } public class SelectionForm extends Form<ConstraintSet> { private static final long serialVersionUID = -4835473062143674510L; public SelectionForm(String aId) { super(aId, Model.of((ConstraintSet) null)); LoadableDetachableModel<List<ConstraintSet>> rulesets = new LoadableDetachableModel<List<ConstraintSet>>() { private static final long serialVersionUID = 1L; @Override protected List<ConstraintSet> load() { return constraintsService.listConstraintSets(ProjectConstraintsPanel.this .getModelObject()); } }; add(new ListChoice<ConstraintSet>("ruleset", SelectionForm.this.getModel(), rulesets) { private static final long serialVersionUID = 1L; { setChoiceRenderer(new ChoiceRenderer<ConstraintSet>("name")); setNullValid(false); } @Override protected void onSelectionChanged(ConstraintSet aNewSelection) { ProjectConstraintsPanel.this.detailForm.setModelObject(aNewSelection); } @Override protected boolean wantOnSelectionChangedNotifications() { return true; } @Override protected CharSequence getDefaultChoice(String aSelectedValue) { return ""; } }); } } public class DetailForm extends Form<ConstraintSet> { private static final long serialVersionUID = 8696334789027911595L; private TextArea<String> script; public DetailForm(String aId) { super(aId, new CompoundPropertyModel<ConstraintSet>(new EntityModel<ConstraintSet>(null))); TextField<String> constraintNameTextField = new TextField<>("name"); add(constraintNameTextField); add(script = new TextArea<String>("script", new LoadableDetachableModel<String>() { private static final long serialVersionUID = 1L; @Override protected String load() { try { return constraintsService.readConstrainSet(DetailForm.this.getModelObject()); } catch (IOException e) { // Cannot call "Component.error()" here - it causes a // org.apache.wicket.WicketRuntimeException: Cannot modify component // hierarchy after render phase has started (page version cant change then // anymore) LOG.error("Unable to load script", e); return "Unable to load script: " + ExceptionUtils.getRootCauseMessage(e); } } })); // Script not editable - if we remove this flag, then the script area will not update // when switching set selection script.setEnabled(false); final IModel<String> exportFilenameModel = new Model<>(); final IModel<File> exportFileModel = new LoadableDetachableModel<File>() { private static final long serialVersionUID = 840863954694163375L; @Override protected File load() { try { // Use the name of the constraints set instead of the ID under which the // file is saved internally. exportFilenameModel.setObject(DetailForm.this.getModelObject().getName()); return constraintsService.exportConstraintAsFile(DetailForm.this .getModelObject()); } catch (IOException e) { throw new WicketRuntimeException(e); } } }; add(new DownloadLink("export", exportFileModel, exportFilenameModel).setDeleteAfterDownload(true)); Button deleteButton = new Button("delete", new StringResourceModel("label")) { private static final long serialVersionUID = -1195565364207114557L; @Override public void onSubmit() { constraintsService.removeConstraintSet(DetailForm.this.getModelObject()); DetailForm.this.setModelObject(null); selectionForm.setModelObject(null); } @Override protected void onConfigure() { super.onConfigure(); setVisible(DetailForm.this.getModelObject().getId() >= 0); } }; // Add check to prevent accidental delete operation deleteButton.add(new AttributeModifier("onclick", "if(!confirm('Do you really want to delete this Constraints rule?')) return false;")); add(deleteButton); add(new Button("save", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; @Override public void onSubmit() { // Actually nothing to do here. Wicket will transfer the values from the // form into the model object and Hibernate will persist it } @Override public void validate() { super.validate(); //Checking if the name provided already exists or not if(constraintsService.existConstraintSet(constraintNameTextField.getInput(), ProjectConstraintsPanel.this.getModelObject()) && !constraintNameTextField.getInput().equals(constraintNameTextField.getModelObject())){ error("Provided name for Constraint already exists, please choose a different name"); } } }); add(new Button("cancel", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; { // Avoid saving data setDefaultFormProcessing(false); setVisible(true); } @Override public void onSubmit() { DetailForm.this.setModelObject(null); } }); } @Override protected void onConfigure() { super.onConfigure(); setVisible(getModelObject() != null); } } public class ImportForm extends Form<Void> { private static final long serialVersionUID = 8121850699963791359L; private List<FileUpload> uploads; public ImportForm(String aId) { super(aId); add(new FileUploadField("uploads", PropertyModel.<List<FileUpload>> of(this, "uploads"))); add(new Button("import", new StringResourceModel("label")) { private static final long serialVersionUID = 1L; @Override public void onSubmit() { try { importAction(); } catch (ParseException e) { error("Exception while parsing the constraint rules file. Please check it. " + ExceptionUtils.getRootCauseMessage(e)); } catch (IOException e) { error("Unable to read constraints file " + ExceptionUtils.getRootCauseMessage(e)); } } }); } private void importAction() throws ParseException, IOException { Project project = ProjectConstraintsPanel.this.getModelObject(); if (project.getId() == 0) { error("Project not yet created, please save project Details!"); return; } if (isEmpty(uploads)) { error("No document is selected to upload, please select a document first"); return; } for (FileUpload constraintRulesFile : uploads) { // Checking if file is OK as per Constraints Grammar specification boolean constraintRuleFileIsOK = false; //Handling Windows BOM BOMInputStream bomInputStream; bomInputStream = new BOMInputStream(constraintRulesFile.getInputStream(), false); ConstraintsGrammar parser = new ConstraintsGrammar(bomInputStream); // utfInputStream = new // InputStreamReader(constraintRulesFile.getInputStream(),"UTF-8"); // ConstraintsGrammar parser = new ConstraintsGrammar(utfInputStream); parser.Parse(); constraintRuleFileIsOK = true; // Persist rules if (constraintRuleFileIsOK) { try { ConstraintSet constraintSet = new ConstraintSet(); constraintSet.setProject(ProjectConstraintsPanel.this.getModelObject()); //Check if ConstraintSet already exists or not String constraintFilename = constraintRulesFile.getClientFileName(); if(constraintsService.existConstraintSet(constraintFilename, project)){ constraintFilename = copyConstraintName(constraintsService,constraintFilename); } constraintSet.setName(constraintFilename); constraintsService.createConstraintSet(constraintSet); constraintsService.writeConstraintSet(constraintSet, constraintRulesFile.getInputStream()); detailForm.setModelObject(constraintSet); selectionForm.setModelObject(constraintSet); } catch (IOException e) { detailForm.setModelObject(null); error("Unable to write constraints file " + ExceptionUtils.getRootCauseMessage(e)); } } } } /** * Checks if name exists, if yes, creates an alternate name for ConstraintSet */ private String copyConstraintName(ConstraintsService aConstraintsService, String constraintFilename) { String betterConstraintName = "copy_of_" + constraintFilename; int i = 1; while (true) { if (aConstraintsService.existConstraintSet(betterConstraintName, ProjectConstraintsPanel.this.getModelObject())) { betterConstraintName = "copy_of_" + constraintFilename + "(" + i + ")"; i++; } else { return betterConstraintName; } } } } }