001 package de.deepamehta.plugins.typeeditor; 002 003 import de.deepamehta.core.Association; 004 import de.deepamehta.core.Topic; 005 import de.deepamehta.core.Type; 006 import de.deepamehta.core.model.AssociationModel; 007 import de.deepamehta.core.model.AssociationDefinitionModel; 008 import de.deepamehta.core.osgi.PluginActivator; 009 import de.deepamehta.core.service.Directive; 010 import de.deepamehta.core.service.Directives; 011 import de.deepamehta.core.service.event.PostUpdateAssociationListener; 012 import de.deepamehta.core.service.event.PreDeleteAssociationListener; 013 014 import java.util.logging.Logger; 015 016 017 018 public 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 String childTypeUri = fetchChildType(assoc).getUri(); 065 // Note: the assoc def's ID is already known. Setting it explicitely 066 // prevents the core from creating the underlying association. 067 AssociationDefinitionModel assocDef = new AssociationDefinitionModel( 068 assoc.getId(), assoc.getUri(), assoc.getTypeUri(), 069 parentType.getUri(), childTypeUri, "dm4.core.one", "dm4.core.one", 070 null // viewConfigModel=null 071 ); 072 logger.info("### Adding association definition \"" + childTypeUri + "\" to type \"" + parentType.getUri() + 073 "\" (" + assocDef + ")"); 074 // 075 parentType.addAssocDef(assocDef); 076 // 077 addUpdateTypeDirective(parentType); 078 } 079 080 private void updateAssocDef(Association assoc) { 081 Type parentType = fetchParentType(assoc); 082 AssociationDefinitionModel assocDef = dms.getTypeStorage().fetchAssociationDefinition(assoc); 083 logger.info("### Updating association definition \"" + assocDef.getChildTypeUri() + "\" of type \"" + 084 parentType.getUri() + "\" (" + assocDef + ")"); 085 // 086 parentType.updateAssocDef(assocDef); 087 // 088 addUpdateTypeDirective(parentType); 089 } 090 091 private void removeAssocDef(Association assoc) { 092 Type parentType = fetchParentType(assoc); 093 String childTypeUri = fetchChildType(assoc).getUri(); 094 logger.info("### Removing association definition \"" + childTypeUri + "\" from type \"" + parentType.getUri() + 095 "\""); 096 // 097 dms.getTypeStorage().removeAssociationDefinitionFromMemoryAndRebuildSequence(parentType, childTypeUri); 098 // 099 addUpdateTypeDirective(parentType); 100 } 101 102 103 104 // === Helper === 105 106 private boolean isAssocDef(AssociationModel assoc) { 107 String typeUri = assoc.getTypeUri(); 108 if (!typeUri.equals("dm4.core.aggregation_def") && 109 !typeUri.equals("dm4.core.composition_def")) { 110 return false; 111 } 112 // 113 if (assoc.hasSameRoleTypeUris()) { 114 return false; 115 } 116 // 117 if (assoc.getRoleModel("dm4.core.parent_type") == null || 118 assoc.getRoleModel("dm4.core.child_type") == null) { 119 return false; 120 } 121 // 122 return true; 123 } 124 125 // ### TODO: adding the UPDATE directive should be the responsibility of a type. The Type interface's 126 // ### addAssocDef(), updateAssocDef(), and removeAssocDef() methods should have a "directives" parameter. 127 private void addUpdateTypeDirective(Type type) { 128 if (type.getTypeUri().equals("dm4.core.topic_type")) { 129 Directives.get().add(Directive.UPDATE_TOPIC_TYPE, type); 130 } else if (type.getTypeUri().equals("dm4.core.assoc_type")) { 131 Directives.get().add(Directive.UPDATE_ASSOCIATION_TYPE, type); 132 } 133 // Note: no else here as error check already performed in fetchParentType() 134 } 135 136 // --- 137 138 private Type fetchParentType(Association assoc) { 139 Topic type = dms.getTypeStorage().fetchParentType(assoc); 140 String typeUri = type.getTypeUri(); 141 if (typeUri.equals("dm4.core.topic_type")) { 142 return dms.getTopicType(type.getUri()); 143 } else if (typeUri.equals("dm4.core.assoc_type")) { 144 return dms.getAssociationType(type.getUri()); 145 } else { 146 throw new RuntimeException("Invalid association definition: the dm4.core.parent_type " + 147 "player is not a type but of type \"" + typeUri + "\" (" + assoc + ")"); 148 } 149 } 150 151 private Topic fetchChildType(Association assoc) { 152 return dms.getTypeStorage().fetchChildType(assoc); 153 } 154 }