/* * #! * 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.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import net.ontopia.utils.ObjectUtils; import ontopoly.model.AssociationType; import ontopoly.model.FieldAssignment; import ontopoly.model.FieldDefinition; import ontopoly.model.IdentityField; import ontopoly.model.NameField; import ontopoly.model.NameType; import ontopoly.model.OccurrenceField; import ontopoly.model.OccurrenceType; import ontopoly.model.QueryField; import ontopoly.model.RoleField; import ontopoly.model.Topic; import ontopoly.model.TopicType; import ontopoly.models.FieldAssignmentModel; import ontopoly.models.FieldDefinitionModel; import ontopoly.models.MutableLoadableDetachableModel; import ontopoly.models.TopicTypeModel; import ontopoly.pages.AbstractOntopolyPage; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.IAjaxIndicatorAware; import org.apache.wicket.ajax.markup.html.AjaxFallbackLink; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.ResourceModel; public class FieldsEditor extends Panel { protected static final boolean ISSUE_329_ENABLED = true; TopicTypeModel topicTypeModel; boolean readonly; ListView<FieldAssignmentModel> listView; MutableLoadableDetachableModel<List<FieldAssignmentModel>> fieldAssignmentModels; public FieldsEditor(String id, TopicTypeModel _topicTypeModel, final boolean readonly) { super(id); this.topicTypeModel = _topicTypeModel; this.readonly = readonly; setOutputMarkupId(true); // existing fields this.fieldAssignmentModels = new MutableLoadableDetachableModel<List<FieldAssignmentModel>>() { @Override protected List<FieldAssignmentModel> load() { List<FieldAssignment> fieldAssignments = topicTypeModel.getTopicType().getFieldAssignments(); return FieldAssignmentModel.wrapInFieldAssignmentModels(fieldAssignments); } }; this.listView = new ListView<FieldAssignmentModel>("existingFields", fieldAssignmentModels) { public void populateItem(final ListItem<FieldAssignmentModel> item) { FieldAssignmentModel fieldAssignmentModel = item.getModelObject(); item.setRenderBodyOnly(true); Component component = new FieldsEditorExistingPanel("field", topicTypeModel, fieldAssignmentModel, readonly) { @Override protected void onMoveAfter(FieldAssignmentModel fam_dg, FieldAssignmentModel fam_do, AjaxRequestTarget target) { // notify parent onUpdate(target); } @Override protected void onRemove(FieldAssignmentModel fam, AjaxRequestTarget target) { TopicType topicType = topicTypeModel.getTopicType(); FieldAssignment fieldAssignment = fam.getFieldAssignment(); topicType.removeField(fieldAssignment.getFieldDefinition()); // notify parent onUpdate(target); } }; component.setRenderBodyOnly(true); item.add(component); } }; listView.setReuseItems(true); add(listView); WebMarkupContainer actionsContainer = new WebMarkupContainer("actions") { @Override public boolean isVisible() { return !FieldsEditor.this.readonly; } }; add(actionsContainer); actionsContainer.add(new FieldDefinitionTypeLink("names") { @Override protected List<NameField> getFieldDefinitions() { List<NameField> fields = topicTypeModel.getTopicType().getTopicMap().getNameFields(); filterFieldDefinitions(fields); return fields; } }); actionsContainer.add(new OntopolyImageLink("create-name-field", "create.gif", new ResourceModel("create.new.name.type")) { @Override public void onClick(AjaxRequestTarget target) { TopicType topicType = topicTypeModel.getTopicType(); NameType nameType = topicType.createNameType(); redirectToTopic(nameType); } }); actionsContainer.add(new FieldDefinitionTypeLink("occurrences") { @Override protected List<OccurrenceField> getFieldDefinitions() { List<OccurrenceField> fields = topicTypeModel.getTopicType().getTopicMap().getOccurrenceFields(); filterFieldDefinitions(fields); return fields; } }); actionsContainer.add(new OntopolyImageLink("create-occurrence-field", "create.gif", new ResourceModel("create.new.occurrence.type")) { @Override public void onClick(AjaxRequestTarget target) { TopicType topicType = topicTypeModel.getTopicType(); OccurrenceType occurrenceType = topicType.createOccurrenceType(); redirectToTopic(occurrenceType); } }); actionsContainer.add(new FieldDefinitionTypeLink("associations") { @Override protected List<? extends FieldDefinition> getFieldDefinitions() { List<RoleField> fields = topicTypeModel.getTopicType().getTopicMap().getRoleFields(); filterFieldDefinitions(fields); return fields; } }); actionsContainer.add(new OntopolyImageLink("create-role-field", "create.gif", new ResourceModel("create.new.association.type")) { @Override public void onClick(AjaxRequestTarget target) { TopicType topicType = topicTypeModel.getTopicType(); AssociationType associationType = topicType.createAssociationType(); redirectToTopic(associationType); } }); actionsContainer.add(new FieldDefinitionTypeLink("identities") { @Override protected List<? extends FieldDefinition> getFieldDefinitions() { List<IdentityField> fields = topicTypeModel.getTopicType().getTopicMap().getIdentityFields(); filterFieldDefinitions(fields); return fields; } }); actionsContainer.add(new FieldDefinitionTypeLink("queries") { @Override protected List<? extends FieldDefinition> getFieldDefinitions() { List<QueryField> fields = topicTypeModel.getTopicType().getTopicMap().getQueryFields(); filterFieldDefinitions(fields); return fields; } @Override public boolean isVisible() { return ISSUE_329_ENABLED; } }); actionsContainer.add(new OntopolyImageLink("create-query-field", "create.gif", new ResourceModel("create.new.query.field")) { @Override public void onClick(AjaxRequestTarget target) { TopicType topicType = topicTypeModel.getTopicType(); QueryField queryField = topicType.createQueryField(); redirectToTopic(queryField); } @Override public boolean isVisible() { return ISSUE_329_ENABLED; } }); List<FieldDefinitionModel> fields = Collections.emptyList(); add(createListView(fields)); } private void filterFieldDefinitions(List<? extends FieldDefinition> result) { AbstractOntopolyPage page = (AbstractOntopolyPage)getPage(); page.filterTopics(result); } private void redirectToTopic(Topic topic) { AbstractOntopolyPage page = (AbstractOntopolyPage)getPage(); setResponsePage(page.getPageClass(topic), page.getPageParameters(topic)); setRedirect(true); } private ListView<FieldDefinitionModel> createListView(final List<FieldDefinitionModel> fieldDefinitionModels) { ListView<FieldDefinitionModel> listView = new ListView<FieldDefinitionModel>("addFields", fieldDefinitionModels) { public void populateItem(final ListItem<FieldDefinitionModel> item) { FieldDefinitionModel fieldDefinitionModel = item.getModelObject(); //item.setRenderBodyOnly(true); Component component = new FieldsEditorAddPanel("field", topicTypeModel, fieldDefinitionModel) { @Override protected void onAddField(TopicTypeModel topicTypeModel, FieldDefinitionModel fieldDefinitionModel, AjaxRequestTarget target) { // add field to topic type topicTypeModel.getTopicType().addField(fieldDefinitionModel.getFieldDefinition()); // remove field definition from available list fieldDefinitionModels.remove(fieldDefinitionModel); @SuppressWarnings("rawtypes") ListView pListView = (ListView)item.getParent(); pListView.removeAll(); onUpdate(target); } }; //component.setRenderBodyOnly(true); item.add(component); } }; listView.setOutputMarkupId(true); listView.setReuseItems(true); return listView; } private String selectedTypeLinkId; private void replaceListView(FieldDefinitionTypeLink typeLink) { String typeLinkId = typeLink.getMarkupId(); ListView<FieldDefinitionModel> afListView; if (ObjectUtils.different(typeLinkId, selectedTypeLinkId)) { // replaces the existing listview with a new one selectedTypeLinkId = typeLinkId; afListView = createListView(filterAndWrapInFieldDefinitions(typeLink.getFieldDefinitions())); } else { List<FieldDefinitionModel> fields = Collections.emptyList(); afListView = createListView(fields); selectedTypeLinkId = null; } replace(afListView); } private List<FieldDefinitionModel> filterAndWrapInFieldDefinitions(List<? extends FieldDefinition> fieldDefinitions) { // resolve existing field definitions List<FieldAssignmentModel> fams = fieldAssignmentModels.getObject(); Set<FieldDefinition> existingFieldDefinitions = new HashSet<FieldDefinition>(fams.size()); Iterator<FieldAssignmentModel> iter = fams.iterator(); while (iter.hasNext()) { FieldAssignmentModel fieldAssignmentModel = iter.next(); existingFieldDefinitions.add(fieldAssignmentModel.getFieldAssignment().getFieldDefinition()); } // filter and sort field definitions List<FieldDefinitionModel> result = new ArrayList<FieldDefinitionModel>(fieldDefinitions.size()); Iterator<? extends FieldDefinition> fditer = fieldDefinitions.iterator(); while (fditer.hasNext()) { FieldDefinition fieldDefinition = fditer.next(); if (!existingFieldDefinitions.contains(fieldDefinition)) result.add(new FieldDefinitionModel(fieldDefinition)); } Collections.sort(result, new Comparator<FieldDefinitionModel>() { public int compare(FieldDefinitionModel o1, FieldDefinitionModel o2) { FieldDefinition fd1 = o1.getFieldDefinition(); FieldDefinition fd2 = o2.getFieldDefinition(); return ObjectUtils.compare(fd1.getFieldName(), fd2.getFieldName()); } }); return result; } private abstract class FieldDefinitionTypeLink extends AjaxFallbackLink<Object> implements IAjaxIndicatorAware { FieldDefinitionTypeLink(String id) { super(id); } protected abstract List<? extends FieldDefinition> getFieldDefinitions(); @Override public void onClick(AjaxRequestTarget target) { replaceListView(this); target.addComponent(FieldsEditor.this); } public String getAjaxIndicatorMarkupId() { return "ajaxIndicator"; } } protected void onUpdate(AjaxRequestTarget target) { listView.removeAll(); fieldAssignmentModels.detach(); // make sure list of field assignments is reloaded target.addComponent(FieldsEditor.this); } @Override public void onDetach() { topicTypeModel.detach(); // listView.detach(); // fieldAssignmentModels.detach(); // addFieldsContainer.detach(); super.onDetach(); } }