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