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            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    }