001package de.deepamehta.plugins.typeeditor; 002 003import de.deepamehta.core.Association; 004import de.deepamehta.core.Topic; 005import de.deepamehta.core.Type; 006import de.deepamehta.core.model.AssociationModel; 007import de.deepamehta.core.model.AssociationDefinitionModel; 008import de.deepamehta.core.osgi.PluginActivator; 009import de.deepamehta.core.service.Directive; 010import de.deepamehta.core.service.Directives; 011import de.deepamehta.core.service.event.PostUpdateAssociationListener; 012import de.deepamehta.core.service.event.PreDeleteAssociationListener; 013 014import java.util.logging.Logger; 015 016 017 018public class TypeEditorPlugin extends PluginActivator implements PostUpdateAssociationListener, 019 PreDeleteAssociationListener { 020 021 // ---------------------------------------------------------------------------------------------- Instance Variables 022 023 private Logger logger = Logger.getLogger(getClass().getName()); 024 025 // -------------------------------------------------------------------------------------------------- Public Methods 026 027 028 029 // ******************************** 030 // *** Listener Implementations *** 031 // ******************************** 032 033 034 035 @Override 036 public void postUpdateAssociation(Association assoc, AssociationModel oldModel) { 037 if (isAssocDef(assoc.getModel())) { 038 if (isAssocDef(oldModel)) { 039 updateAssocDef(assoc); 040 } else { 041 createAssocDef(assoc); 042 } 043 } else if (isAssocDef(oldModel)) { 044 removeAssocDef(assoc); 045 } 046 } 047 048 // Note: we listen to the PRE event here, not the POST event. At POST time the assocdef sequence might be 049 // interrupted, which would result in a corrupted sequence once rebuild. (Due to the interruption, while 050 // rebuilding not all segments would be catched for deletion and recreated redundantly -> ambiguity.) 051 @Override 052 public void preDeleteAssociation(Association assoc) { 053 if (isAssocDef(assoc.getModel())) { 054 removeAssocDef(assoc); 055 } 056 } 057 058 059 060 // ------------------------------------------------------------------------------------------------- Private Methods 061 062 private void createAssocDef(Association assoc) { 063 Type parentType = fetchParentType(assoc); 064 AssociationDefinitionModel assocDef = dms.getTypeStorage().createAssociationDefinition(assoc); 065 logger.info("### Adding association definition \"" + assocDef.getChildTypeUri() + "\" to type \"" + 066 parentType.getUri() + "\" (" + assocDef + ")"); 067 // 068 parentType.addAssocDef(assocDef); 069 // 070 addUpdateTypeDirective(parentType); 071 } 072 073 private void updateAssocDef(Association assoc) { 074 Type parentType = fetchParentType(assoc); 075 AssociationDefinitionModel assocDef = dms.getTypeStorage().fetchAssociationDefinition(assoc); 076 logger.info("### Updating association definition \"" + assocDef.getChildTypeUri() + "\" of type \"" + 077 parentType.getUri() + "\" (" + assocDef + ")"); 078 // 079 parentType.updateAssocDef(assocDef); 080 // 081 addUpdateTypeDirective(parentType); 082 } 083 084 private void removeAssocDef(Association assoc) { 085 Type parentType = fetchParentType(assoc); 086 String childTypeUri = fetchChildType(assoc).getUri(); 087 logger.info("### Removing association definition \"" + childTypeUri + "\" from type \"" + parentType.getUri() + 088 "\""); 089 // 090 dms.getTypeStorage().removeAssociationDefinitionFromMemoryAndRebuildSequence(parentType, childTypeUri); 091 // 092 addUpdateTypeDirective(parentType); 093 } 094 095 096 097 // === Helper === 098 099 private boolean isAssocDef(AssociationModel assoc) { 100 String typeUri = assoc.getTypeUri(); 101 if (!typeUri.equals("dm4.core.aggregation_def") && 102 !typeUri.equals("dm4.core.composition_def")) { 103 return false; 104 } 105 // 106 if (assoc.hasSameRoleTypeUris()) { 107 return false; 108 } 109 if (assoc.getRoleModel("dm4.core.parent_type") == null || 110 assoc.getRoleModel("dm4.core.child_type") == null) { 111 return false; 112 } 113 // 114 return true; 115 } 116 117 // ### TODO: adding the UPDATE directive should be the responsibility of a type. The Type interface's 118 // ### addAssocDef(), updateAssocDef(), and removeAssocDef() methods should have a "directives" parameter. 119 private void addUpdateTypeDirective(Type type) { 120 if (type.getTypeUri().equals("dm4.core.topic_type")) { 121 Directives.get().add(Directive.UPDATE_TOPIC_TYPE, type); 122 } else if (type.getTypeUri().equals("dm4.core.assoc_type")) { 123 Directives.get().add(Directive.UPDATE_ASSOCIATION_TYPE, type); 124 } 125 // Note: no else here as error check already performed in fetchParentType() 126 } 127 128 // --- 129 130 private Type fetchParentType(Association assoc) { 131 Topic type = dms.getTypeStorage().fetchParentType(assoc); 132 String typeUri = type.getTypeUri(); 133 if (typeUri.equals("dm4.core.topic_type")) { 134 return dms.getTopicType(type.getUri()); 135 } else if (typeUri.equals("dm4.core.assoc_type")) { 136 return dms.getAssociationType(type.getUri()); 137 } else { 138 throw new RuntimeException("Invalid association definition: the dm4.core.parent_type " + 139 "player is not a type but of type \"" + typeUri + "\" (" + assoc + ")"); 140 } 141 } 142 143 private Topic fetchChildType(Association assoc) { 144 return dms.getTypeStorage().fetchChildType(assoc); 145 } 146}