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}