/*
* #!
* Ontopoly Editor
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* 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 ontopoly.components;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import ontopoly.model.EditMode;
import ontopoly.model.FieldInstance;
import ontopoly.model.InterfaceControl;
import ontopoly.model.RoleField;
import ontopoly.model.Topic;
import ontopoly.model.TopicMap;
import ontopoly.model.TopicType;
import ontopoly.models.FieldInstanceModel;
import ontopoly.models.FieldValueModel;
import ontopoly.models.FieldsViewModel;
import ontopoly.models.PossiblePlayersModel;
import ontopoly.models.RoleFieldModel;
import ontopoly.models.TopicModel;
import ontopoly.pages.AbstractOntopolyPage;
import ontopoly.pages.ModalFindPage;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
public class FieldInstanceAssociationNaryField extends Panel {
protected int arity;
protected final FieldValueModel fieldValueModel;
protected final Map<RoleFieldModel,TopicModel<Topic>> selectedPlayers = new HashMap<RoleFieldModel,TopicModel<Topic>>();
protected final FieldInstanceAssociationNaryPanel parentPanel;
protected boolean needsUpdate;
protected final RoleFieldModel currentFieldModel;
protected final TopicModel<Topic> currentTopicModel;
public FieldInstanceAssociationNaryField(String id,
FieldInstanceAssociationNaryPanel _parentPanel,
RoleFieldModel roleFieldModel, List<RoleFieldModel> otherRoleFieldModels,
FieldValueModel _fieldValueModel,
FieldsViewModel fieldsViewModel,
final boolean readonly, boolean traversable, final int arity) {
super(id);
this.fieldValueModel = _fieldValueModel;
this.parentPanel = _parentPanel;
this.arity = arity;
FieldInstanceModel fieldInstanceModel = fieldValueModel.getFieldInstanceModel();
// register current player
this.currentFieldModel = roleFieldModel;
this.currentTopicModel = new TopicModel<Topic>(fieldInstanceModel.getFieldInstance().getInstance());
selectedPlayers.put(roleFieldModel, currentTopicModel);
RoleField.ValueIF fieldValue = (RoleField.ValueIF)fieldValueModel.getFieldValue();
RepeatingView rv = new RepeatingView("roles");
rv.setVisible(!readonly || fieldValueModel.isExistingValue());
add(rv);
Iterator<RoleFieldModel> oiter = otherRoleFieldModels.iterator();
while (oiter.hasNext()) {
final RoleFieldModel ofieldModel = oiter.next();
RoleField ofield = ofieldModel.getRoleField();
final WebMarkupContainer parent = new WebMarkupContainer(rv.newChildId()) {
@Override
public boolean isVisible() {
// hide if read-only and not complete
if (readonly && selectedPlayers.size() != arity)
return false;
else
return true;
}
};
parent.setOutputMarkupId(true);
rv.add(parent);
parent.add(new Label("label", new Model<String>(ofield.getRoleType().getName())));
//! parent.add(new Label("label", new Model(ofield.getFieldName())));
// register other player
Topic topic = (fieldValue == null ? null : fieldValue.getPlayer(ofield, fieldInstanceModel.getFieldInstance().getInstance()));
final TopicModel<Topic> topicModel = new TopicModel<Topic>(topic);
// NOTE: should not use same model as selected model as the model would then be updated immediately
selectedPlayers.put(ofieldModel, new TopicModel<Topic>(topic));
TopicLink<Topic> playerLink = new TopicLink<Topic>("player", topicModel);
playerLink.setEnabled(traversable);
playerLink.setVisible(topic != null);
parent.add(playerLink);
EditMode editMode = ofield.getEditMode();
final boolean allowAdd = !editMode.isNewValuesOnly();
final boolean allowCreate = !editMode.isExistingValuesOnly();
if (readonly || fieldValueModel.isExistingValue() || !allowAdd) {
// unused components
parent.add(new Label("select").setVisible(false));
parent.add(new Label("find").setVisible(false));
parent.add(new Label("findModal").setVisible(false));
} else {
InterfaceControl interfaceControl = ofield.getInterfaceControl();
if (interfaceControl.isAutoComplete()) {
final AssociationFieldAutoCompleteTextField autoCompleteField
= new AssociationFieldAutoCompleteTextField("select", new TopicModel<Topic>(null), ofieldModel) {
@Override
protected void filterPlayers(List<Topic> players) {
AbstractOntopolyPage page = (AbstractOntopolyPage)getPage();
page.filterTopics(players);
}
@Override
protected void onTopicSelected(Topic topic) {
topicModel.setObject(topic);
boolean changesMade = onNewSelection(ofieldModel, topic);
// replace ourselves with a topic link
if (changesMade)
parent.replace(new TopicLink<Topic>("select", new TopicModel<Topic>(topic)));
}
};
autoCompleteField.getTextField().add(new AjaxFormComponentUpdatingBehavior("onchange") {
@Override
protected void onUpdate(AjaxRequestTarget target) {
if (needsUpdate)
FieldInstanceAssociationNaryField.this.onUpdate(target);
else
target.addComponent(parent);
}
});
autoCompleteField.setOutputMarkupId(true);
parent.add(autoCompleteField);
// unused components
parent.add(new Label("find").setVisible(false));
parent.add(new Label("findModal").setVisible(false));
} else if (interfaceControl.isDropDownList()) {
PossiblePlayersModel choicesModel = new PossiblePlayersModel(fieldInstanceModel, ofieldModel) {
@Override
protected void filterPlayers(Collection<Topic> players) {
AbstractOntopolyPage page = (AbstractOntopolyPage)getPage();
page.filterTopics(players);
}
};
TopicDropDownChoice<Topic> choice = new TopicDropDownChoice<Topic>("select", topicModel, choicesModel) {
@Override
protected void onModelChanged() {
super.onModelChanged();
onNewSelection(ofieldModel, getModel().getObject());
}
};
choice.setOutputMarkupId(true);
choice.add(new AjaxFormComponentUpdatingBehavior("onchange") {
@Override
protected void onUpdate(AjaxRequestTarget target) {
FieldInstanceAssociationNaryField.this.onUpdate(target);
}
@Override
protected void onError(AjaxRequestTarget target, RuntimeException e) {
FieldInstanceAssociationNaryField.this.onError(target, e);
}
});
parent.add(choice);
// unused components
parent.add(new Label("find").setVisible(false));
parent.add(new Label("findModal").setVisible(false));
} else if (interfaceControl.isSearchDialog() || interfaceControl.isBrowseDialog()) {
// unused components
parent.add(new TopicLink<Topic>("select", topicModel));
// "search"/"browse" button
final ModalWindow findModal = new ModalWindow("findModal");
parent.add(findModal);
int activeTab = (interfaceControl.isSearchDialog() ?
ModalFindPage.ACTIVE_TAB_SEARCH : ModalFindPage.ACTIVE_TAB_BROWSE);
findModal.setContent(new ModalFindPage<String>(findModal.getContentId(), fieldInstanceModel, activeTab) {
@Override
protected boolean isMaxOneCardinality() {
return true;
}
@Override
protected void onSelectionConfirmed(AjaxRequestTarget target, Collection<String> selected) {
if (!selected.isEmpty()) {
String topicId = selected.iterator().next();
TopicMap topicMap = fieldValueModel.getFieldInstanceModel().getFieldInstance().getInstance().getTopicMap();
Topic topic = topicMap.getTopicById(topicId);
topicModel.setObject(topic);
onNewSelection(ofieldModel, topic);
if (needsUpdate)
FieldInstanceAssociationNaryField.this.onUpdate(target);
else
target.addComponent(parent);
}
}
@Override
protected void onCloseCancel(AjaxRequestTarget target) {
findModal.close(target);
}
@Override
protected void onCloseOk(AjaxRequestTarget target) {
findModal.close(target);
}
});
findModal.setTitle(new ResourceModel("ModalWindow.title.find.topic").getObject().toString());
findModal.setCookieName("findModal");
OntopolyImageLink findButton = new OntopolyImageLink("find", "search.gif", new ResourceModel("icon.search.find-topic")) {
@Override
public boolean isVisible() {
return !readonly;
}
@Override
public void onClick(AjaxRequestTarget target) {
findModal.show(target);
}
};
parent.add(findButton);
} else {
throw new RuntimeException("Unsupported interface control: " + interfaceControl);
}
}
// create button
if (readonly || fieldValueModel.isExistingValue() || !allowCreate) {
parent.add(new Label("create").setVisible(false));
} else {
// always use popup window
int createAction = FieldInstanceCreatePlayerPanel.CREATE_ACTION_POPUP;
FieldInstanceCreatePlayerPanel createPanel = new FieldInstanceCreatePlayerPanel("create", fieldInstanceModel, fieldsViewModel, ofieldModel, parentPanel, createAction) {
@Override
protected Topic createInstance(TopicType topicType) {
Topic currentTopic = currentTopicModel.getTopic();
RoleField currentField = currentFieldModel.getRoleField();
RoleField createField = ofieldModel.getRoleField();
Topic createdTopic = null;
// check with page to see if add is allowed
AbstractOntopolyPage page = (AbstractOntopolyPage)getPage();
if (page.isCreateAllowed(currentTopic, currentField, topicType, createField)) {
// create a new topic instance
createdTopic = topicType.createInstance(null);
topicModel.setObject(createdTopic);
performNewSelection(ofieldModel, createdTopic);
}
return createdTopic;
}
@Override
protected void performNewSelection(RoleFieldModel ofieldModel, Topic selectedTopic) {
FieldInstanceAssociationNaryField.this.performNewSelection(ofieldModel, selectedTopic);
}
@Override
protected void hideInstancePage(AjaxRequestTarget target) {
if (needsUpdate)
FieldInstanceAssociationNaryField.this.onUpdate(target);
else
target.addComponent(parent);
}
};
createPanel.setOutputMarkupId(true);
parent.add(createPanel);
}
}
}
protected boolean onNewSelection(RoleFieldModel ofieldModel, Topic selectedTopic) {
Topic currentTopic = currentTopicModel.getTopic();
RoleField currentField = currentFieldModel.getRoleField();
RoleField selectedField = ofieldModel.getRoleField();
// check with page to see if add is allowed
AbstractOntopolyPage page = (AbstractOntopolyPage)getPage();
if (page.isAddAllowed(currentTopic, currentField, selectedTopic, selectedField)) {
performNewSelection(ofieldModel, selectedTopic);
return true;
} else {
return false;
}
}
protected void performNewSelection(RoleFieldModel ofieldModel, Topic selectedTopic) {
try {
AbstractOntopolyPage page = (AbstractOntopolyPage)getPage();
// check to see if old state was complete
if (selectedPlayers.size() == arity) {
// remove existing association if old state was complete
RoleField.ValueIF oldValue = createValue();
if (oldValue != null) {
FieldInstance fieldInstance = fieldValueModel.getFieldInstanceModel().getFieldInstance();
fieldInstance.removeValue(oldValue, page.getListener());
}
}
// add new selection
selectedPlayers.put(ofieldModel, new TopicModel<Topic>(selectedTopic));
// check to see if new state was complete
if (selectedPlayers.size() == arity) {
// add new association as state is now complete
RoleField.ValueIF newValue = createValue();
if (newValue != null) {
FieldInstance fieldInstance = fieldValueModel.getFieldInstanceModel().getFieldInstance();
fieldInstance.addValue(newValue, page.getListener());
fieldValueModel.setExistingValue(newValue);
this.needsUpdate = true;
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void onUpdate(AjaxRequestTarget target) {
if (needsUpdate) {
parentPanel.fieldValuesModel.setShowExtraField(false, false);
parentPanel.onUpdate(target);
}
needsUpdate = false;
}
protected void onError(AjaxRequestTarget target, RuntimeException e) {
if (needsUpdate)
parentPanel.onError(target, e);
needsUpdate = false;
}
protected RoleField.ValueIF createValue() {
RoleField.ValueIF value = RoleField.createValue(arity);
Iterator<RoleFieldModel> iter = selectedPlayers.keySet().iterator();
while (iter.hasNext()) {
RoleFieldModel roleFieldModel = iter.next();
TopicModel<Topic> topicModel = selectedPlayers.get(roleFieldModel);
RoleField roleField = roleFieldModel.getRoleField();
Topic topic = topicModel.getTopic();
// if topic is null then the value is not complete, so we return null
if (topic == null) return null;
value.addPlayer(roleField, topic);
}
return value;
}
@Override
public void onDetach() {
fieldValueModel.detach();
currentFieldModel.detach();
currentTopicModel.detach();
for (Map.Entry<RoleFieldModel,TopicModel<Topic>> selectedPlayer : selectedPlayers.entrySet()) {
selectedPlayer.getKey().detach();
selectedPlayer.getValue().detach();
}
super.onDetach();
}
}