package fi.otavanopisto.muikku.plugins.workspace;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import fi.otavanopisto.muikku.plugins.material.MaterialFieldMetaParsingExeption;
import fi.otavanopisto.muikku.plugins.material.dao.QueryMultiSelectFieldOptionDAO;
import fi.otavanopisto.muikku.plugins.material.dao.QuerySelectFieldOptionDAO;
import fi.otavanopisto.muikku.plugins.material.fieldmeta.MultiSelectFieldMeta;
import fi.otavanopisto.muikku.plugins.material.fieldmeta.MultiSelectFieldOptionMeta;
import fi.otavanopisto.muikku.plugins.material.fieldmeta.OrganizerFieldMeta;
import fi.otavanopisto.muikku.plugins.material.fieldmeta.SelectFieldMeta;
import fi.otavanopisto.muikku.plugins.material.fieldmeta.SelectFieldOptionMeta;
import fi.otavanopisto.muikku.plugins.material.fieldmeta.SorterFieldMeta;
import fi.otavanopisto.muikku.plugins.material.model.QueryMultiSelectField;
import fi.otavanopisto.muikku.plugins.material.model.QueryMultiSelectFieldOption;
import fi.otavanopisto.muikku.plugins.material.model.QuerySelectField;
import fi.otavanopisto.muikku.plugins.material.model.QuerySelectFieldOption;
import fi.otavanopisto.muikku.plugins.workspace.dao.WorkspaceMaterialFieldDAO;
import fi.otavanopisto.muikku.plugins.workspace.dao.WorkspaceMaterialMultiSelectFieldAnswerDAO;
import fi.otavanopisto.muikku.plugins.workspace.dao.WorkspaceMaterialOrganizerFieldAnswerDAO;
import fi.otavanopisto.muikku.plugins.workspace.dao.WorkspaceMaterialSelectFieldAnswerDAO;
import fi.otavanopisto.muikku.plugins.workspace.dao.WorkspaceMaterialSorterFieldAnswerDAO;
import fi.otavanopisto.muikku.plugins.workspace.events.WorkspaceMaterialFieldDeleteEvent;
import fi.otavanopisto.muikku.plugins.workspace.events.WorkspaceMaterialFieldUpdateEvent;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialAudioFieldAnswer;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialAudioFieldAnswerClip;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialField;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialFieldAnswer;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialFileFieldAnswer;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialFileFieldAnswerFile;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialMultiSelectFieldAnswer;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialMultiSelectFieldAnswerOption;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialOrganizerFieldAnswer;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialSelectFieldAnswer;
import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialSorterFieldAnswer;
public class WorkspaceMaterialFieldChangeListener {
@Inject
private WorkspaceMaterialFieldDAO workspaceMaterialFieldDAO;
@Inject
private WorkspaceMaterialFieldAnswerController workspaceMaterialFieldAnswerController;
@Inject
private QuerySelectFieldOptionDAO querySelectFieldOptionDAO;
@Inject
private QueryMultiSelectFieldOptionDAO queryMultiSelectFieldOptionDAO;
@Inject
private WorkspaceMaterialSelectFieldAnswerDAO workspaceMaterialSelectFieldAnswerDAO;
@Inject
private WorkspaceMaterialMultiSelectFieldAnswerDAO workspaceMaterialMultiSelectFieldAnswerDAO;
@Inject
private WorkspaceMaterialOrganizerFieldAnswerDAO workspaceMaterialOrganizerFieldAnswerDAO;
@Inject
private WorkspaceMaterialSorterFieldAnswerDAO workspaceMaterialSorterFieldAnswerDAO;
// Create
// Update
// Organizer field
public void onWorkspaceMaterialOrganizerFieldUpdate(@Observes WorkspaceMaterialFieldUpdateEvent event) throws MaterialFieldMetaParsingExeption, WorkspaceMaterialContainsAnswersExeption {
if (event.getMaterialField().getType().equals("application/vnd.muikku.field.organizer")) {
ObjectMapper objectMapper = new ObjectMapper();
OrganizerFieldMeta organizerFieldMeta;
try {
organizerFieldMeta = objectMapper.readValue(event.getMaterialField().getContent(), OrganizerFieldMeta.class);
} catch (IOException e) {
throw new MaterialFieldMetaParsingExeption("Could not parse organizer field meta", e);
}
List<WorkspaceMaterialField> fields = workspaceMaterialFieldDAO.listByQueryField(event.getWorkspaceMaterialField().getQueryField());
for (WorkspaceMaterialField field : fields) {
List<WorkspaceMaterialOrganizerFieldAnswer> answers = workspaceMaterialOrganizerFieldAnswerDAO.listByWorkspaceMaterialField(field);
for (WorkspaceMaterialOrganizerFieldAnswer answer : answers) {
try {
boolean answerModified = false;
HashMap<String, Set<String>> answerObject = objectMapper.readValue(answer.getValue(), new TypeReference<HashMap<String, Set<String>>>() {});
Set<String> terms = new HashSet<String>();
Set<String> categories = answerObject.keySet();
for (String category : categories) {
terms.addAll(answerObject.get(category));
if (!organizerFieldMeta.hasCategoryWithId(category)) {
if (!event.getRemoveAnswers()) {
throw new WorkspaceMaterialContainsAnswersExeption("Could not update organizer field because it contains answers");
}
else {
answerObject.remove(category);
answerModified = true;
}
}
}
for (String term : terms) {
if (!organizerFieldMeta.hasTermWithId(term)) {
if (!event.getRemoveAnswers()) {
throw new WorkspaceMaterialContainsAnswersExeption("Could not update organizer field because it contains answers");
}
else {
Iterator<String> categoryIterator = answerObject.keySet().iterator();
while (categoryIterator.hasNext()) {
Set<String> categoryTerms = answerObject.get(categoryIterator.next());
if (categoryTerms.contains(term)) {
categoryTerms.remove(term);
answerModified = true;
}
}
}
}
}
if (answerModified) {
workspaceMaterialOrganizerFieldAnswerDAO.updateValue(answer, objectMapper.writeValueAsString(answerObject));
}
}
catch (IOException e) {
throw new MaterialFieldMetaParsingExeption("Could not parse organizer field answer meta", e);
}
}
}
}
}
// Sorter field
public void onWorkspaceMaterialSorterFieldUpdate(@Observes WorkspaceMaterialFieldUpdateEvent event) throws MaterialFieldMetaParsingExeption, WorkspaceMaterialContainsAnswersExeption {
if (event.getMaterialField().getType().equals("application/vnd.muikku.field.sorter")) {
ObjectMapper objectMapper = new ObjectMapper();
SorterFieldMeta sorterFieldMeta;
try {
sorterFieldMeta = objectMapper.readValue(event.getMaterialField().getContent(), SorterFieldMeta.class);
} catch (IOException e) {
throw new MaterialFieldMetaParsingExeption("Could not parse sorter field meta", e);
}
List<WorkspaceMaterialField> fields = workspaceMaterialFieldDAO.listByQueryField(event.getWorkspaceMaterialField().getQueryField());
for (WorkspaceMaterialField field : fields) {
List<WorkspaceMaterialSorterFieldAnswer> answers = workspaceMaterialSorterFieldAnswerDAO.listByWorkspaceMaterialField(field);
for (WorkspaceMaterialSorterFieldAnswer answer : answers) {
try {
boolean answerModified = false;
List<String> answerItems = objectMapper.readValue(answer.getValue(), new TypeReference<ArrayList<String>>() {});
for (int i = answerItems.size() - 1; i >= 0; i--) {
String answerItem = answerItems.get(i);
if (!sorterFieldMeta.hasItemWithId(answerItem)) {
if (!event.getRemoveAnswers()) {
throw new WorkspaceMaterialContainsAnswersExeption("Could not update sorter field because it contains answers");
}
else {
answerItems.remove(answerItem);
answerModified = true;
}
}
}
if (answerModified) {
workspaceMaterialSorterFieldAnswerDAO.updateValue(answer, objectMapper.writeValueAsString(answerItems));
}
}
catch (IOException e) {
throw new MaterialFieldMetaParsingExeption("Could not parse sorter field answer meta", e);
}
}
}
}
}
// Select field
public void onWorkspaceMaterialSelectFieldUpdate(@Observes WorkspaceMaterialFieldUpdateEvent event) throws MaterialFieldMetaParsingExeption, WorkspaceMaterialContainsAnswersExeption {
if (event.getMaterialField().getType().equals("application/vnd.muikku.field.select")) {
// Field JSON to metadata object
ObjectMapper objectMapper = new ObjectMapper();
SelectFieldMeta selectFieldMeta;
try {
selectFieldMeta = objectMapper.readValue(event.getMaterialField().getContent(), SelectFieldMeta.class);
} catch (IOException e) {
throw new MaterialFieldMetaParsingExeption("Could not parse select field meta", e);
}
QuerySelectField queryField = (QuerySelectField) event.getWorkspaceMaterialField().getQueryField();
// Ensure that if there are options being removed, they haven't been used as answers
List<WorkspaceMaterialSelectFieldAnswer> deprecatedAnswers = new ArrayList<WorkspaceMaterialSelectFieldAnswer>();
List<QuerySelectFieldOption> oldOptions = querySelectFieldOptionDAO.listByField(queryField);
List<SelectFieldOptionMeta> newOptions = selectFieldMeta.getOptions();
for (SelectFieldOptionMeta newOption : newOptions) {
QuerySelectFieldOption correspondingOption = findSelectOptionByName(oldOptions, newOption.getName());
if (correspondingOption != null) {
oldOptions.remove(correspondingOption);
}
}
for (QuerySelectFieldOption removedOption : oldOptions) {
List<WorkspaceMaterialSelectFieldAnswer> answers = workspaceMaterialSelectFieldAnswerDAO.listByQuerySelectFieldOption(removedOption);
deprecatedAnswers.addAll(answers);
}
// Either delete the answers of now-removed options or keel over gracefully
if (!deprecatedAnswers.isEmpty() && !event.getRemoveAnswers()) {
throw new WorkspaceMaterialContainsAnswersExeption("Could not remove workspace material field because it contains answers");
}
else if (!deprecatedAnswers.isEmpty()) {
for (WorkspaceMaterialSelectFieldAnswer deprecatedAnswer : deprecatedAnswers) {
deleteFieldAnswer(deprecatedAnswer);
}
}
}
}
private QuerySelectFieldOption findSelectOptionByName(List<QuerySelectFieldOption> options, String name) {
for (QuerySelectFieldOption option : options) {
if (StringUtils.equals(option.getName(), name)) {
return option;
}
}
return null;
}
// Multi-select field
public void onWorkspaceMaterialMultiSelectFieldUpdate(@Observes WorkspaceMaterialFieldUpdateEvent event) throws MaterialFieldMetaParsingExeption, WorkspaceMaterialContainsAnswersExeption {
if (event.getMaterialField().getType().equals("application/vnd.muikku.field.multiselect")) {
// Field JSON to metadata object
ObjectMapper objectMapper = new ObjectMapper();
MultiSelectFieldMeta multiSelectFieldMeta;
try {
multiSelectFieldMeta = objectMapper.readValue(event.getMaterialField().getContent(), MultiSelectFieldMeta.class);
} catch (IOException e) {
throw new MaterialFieldMetaParsingExeption("Could not parse multi-select field meta", e);
}
QueryMultiSelectField queryField = (QueryMultiSelectField) event.getWorkspaceMaterialField().getQueryField();
// Ensure that if there are options being removed, they haven't been used as answers
List<WorkspaceMaterialMultiSelectFieldAnswer> deprecatedAnswers = new ArrayList<WorkspaceMaterialMultiSelectFieldAnswer>();
List<QueryMultiSelectFieldOption> oldOptions = queryMultiSelectFieldOptionDAO.listByField(queryField);
List<MultiSelectFieldOptionMeta> newOptions = multiSelectFieldMeta.getOptions();
for (MultiSelectFieldOptionMeta newOption : newOptions) {
QueryMultiSelectFieldOption correspondingOption = findMultiSelectOptionByName(oldOptions, newOption.getName());
if (correspondingOption != null) {
oldOptions.remove(correspondingOption);
}
}
for (QueryMultiSelectFieldOption removedOption : oldOptions) {
List<WorkspaceMaterialMultiSelectFieldAnswer> answers = workspaceMaterialMultiSelectFieldAnswerDAO.listByQueryMultiSelectFieldOption(removedOption);
deprecatedAnswers.addAll(answers);
}
// Either delete the answers of now-removed options or keel over gracefully
if (!deprecatedAnswers.isEmpty() && !event.getRemoveAnswers()) {
throw new WorkspaceMaterialContainsAnswersExeption("Could not remove workspace material field because it contains answers");
}
else if (!deprecatedAnswers.isEmpty()) {
for (WorkspaceMaterialMultiSelectFieldAnswer deprecatedAnswer : deprecatedAnswers) {
deleteFieldAnswer(deprecatedAnswer);
}
}
}
}
private QueryMultiSelectFieldOption findMultiSelectOptionByName(List<QueryMultiSelectFieldOption> options, String name) {
for (QueryMultiSelectFieldOption option : options) {
if (StringUtils.equals(option.getName(), name)) {
return option;
}
}
return null;
}
// Delete
public void onWorkspaceMaterialFieldDelete(@Observes WorkspaceMaterialFieldDeleteEvent event) throws WorkspaceMaterialContainsAnswersExeption {
WorkspaceMaterialField materialField = event.getWorkspaceMaterialField();
List<WorkspaceMaterialFieldAnswer> answers = workspaceMaterialFieldAnswerController.listWorkspaceMaterialFieldAnswersByField(materialField);
if (event.getRemoveAnswers()) {
for (WorkspaceMaterialFieldAnswer answer : answers) {
deleteFieldAnswer(answer);
}
} else {
if (!answers.isEmpty()) {
throw new WorkspaceMaterialContainsAnswersExeption("Could not remove workspace material field because it contains answers");
}
}
}
private void deleteFieldAnswer(WorkspaceMaterialFieldAnswer answer) {
if (answer instanceof WorkspaceMaterialAudioFieldAnswer) {
List<WorkspaceMaterialAudioFieldAnswerClip> audioAnswerClips = workspaceMaterialFieldAnswerController.listWorkspaceMaterialAudioFieldAnswerClipsByFieldAnswer((WorkspaceMaterialAudioFieldAnswer) answer);
for (WorkspaceMaterialAudioFieldAnswerClip audioAnswerClip : audioAnswerClips) {
workspaceMaterialFieldAnswerController.deleteWorkspaceMaterialAudioFieldAnswerClip(audioAnswerClip);
}
}
else if (answer instanceof WorkspaceMaterialFileFieldAnswer) {
List<WorkspaceMaterialFileFieldAnswerFile> fileAnswerFiles = workspaceMaterialFieldAnswerController.listWorkspaceMaterialFileFieldAnswerFilesByFieldAnswer((WorkspaceMaterialFileFieldAnswer) answer);
for (WorkspaceMaterialFileFieldAnswerFile fieldAnswerFile : fileAnswerFiles) {
workspaceMaterialFieldAnswerController.deleteWorkspaceMaterialFileFieldAnswerFile(fieldAnswerFile);
}
}
else if (answer instanceof WorkspaceMaterialMultiSelectFieldAnswer) {
List<WorkspaceMaterialMultiSelectFieldAnswerOption> options = workspaceMaterialFieldAnswerController.listWorkspaceMaterialMultiSelectFieldAnswerOptions((WorkspaceMaterialMultiSelectFieldAnswer) answer);
for (WorkspaceMaterialMultiSelectFieldAnswerOption option : options) {
workspaceMaterialFieldAnswerController.deleteWorkspaceMaterialMultiSelectFieldAnswerOption(option);
}
}
workspaceMaterialFieldAnswerController.deleteWorkspaceMaterialFieldAnswer(answer);
}
}