001    package de.deepamehta.plugins.facets;
002    
003    import de.deepamehta.plugins.facets.model.FacetValue;
004    import de.deepamehta.plugins.facets.service.FacetsService;
005    
006    import de.deepamehta.core.Association;
007    import de.deepamehta.core.AssociationDefinition;
008    import de.deepamehta.core.DeepaMehtaObject;
009    import de.deepamehta.core.RelatedTopic;
010    import de.deepamehta.core.Topic;
011    import de.deepamehta.core.model.AssociationModel;
012    import de.deepamehta.core.model.TopicModel;
013    import de.deepamehta.core.model.TopicRoleModel;
014    import de.deepamehta.core.osgi.PluginActivator;
015    import de.deepamehta.core.service.ClientState;
016    import de.deepamehta.core.service.Directives;
017    import de.deepamehta.core.storage.spi.DeepaMehtaTransaction;
018    
019    import javax.ws.rs.GET;
020    import javax.ws.rs.POST;
021    import javax.ws.rs.PUT;
022    import javax.ws.rs.HeaderParam;
023    import javax.ws.rs.Path;
024    import javax.ws.rs.PathParam;
025    import javax.ws.rs.Produces;
026    import javax.ws.rs.Consumes;
027    
028    import java.util.List;
029    import java.util.logging.Logger;
030    
031    
032    
033    @Path("/facet")
034    @Consumes("application/json")
035    @Produces("application/json")
036    public class FacetsPlugin extends PluginActivator implements FacetsService {
037    
038        // ---------------------------------------------------------------------------------------------- Instance Variables
039    
040        private Logger logger = Logger.getLogger(getClass().getName());
041    
042        // -------------------------------------------------------------------------------------------------- Public Methods
043    
044    
045    
046        // ************************************
047        // *** FacetsService Implementation ***
048        // ************************************
049    
050    
051    
052        @GET
053        @Path("/{facet_type_uri}/topic/{id}")
054        @Override
055        public Topic getFacet(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri) {
056            return getFacet(dms.getTopic(topicId, false), facetTypeUri);        // fetchComposite=false
057        }
058    
059        @Override
060        public Topic getFacet(DeepaMehtaObject object, String facetTypeUri) {
061            // ### TODO: integrity check: is the object an instance of that facet type?
062            return fetchChildTopic(object, getAssocDef(facetTypeUri), true);    // fetchComposite=true
063        }
064    
065        // ---
066    
067        @GET
068        @Path("/multi/{facet_type_uri}/topic/{id}")
069        @Override
070        public List<RelatedTopic> getFacets(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri)
071                                                                                                                    {
072            return getFacets(dms.getTopic(topicId, false), facetTypeUri);       // fetchComposite=false
073        }
074    
075        @Override
076        public List<RelatedTopic> getFacets(DeepaMehtaObject object, String facetTypeUri) {
077            // ### TODO: integrity check: is the object an instance of that facet type?
078            return fetchChildTopics(object, getAssocDef(facetTypeUri), true);   // fetchComposite=true
079        }
080    
081        // ---
082    
083        @POST
084        @Path("/{facet_type_uri}/topic/{id}")
085        @Override
086        public void addFacetTypeToTopic(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri) {
087            dms.createAssociation(new AssociationModel("dm4.core.instantiation",
088                new TopicRoleModel(topicId,      "dm4.core.instance"),
089                new TopicRoleModel(facetTypeUri, "dm4.facets.facet")), null);   // clientState=null
090        }
091    
092        // ---
093    
094        @PUT
095        @Path("/{facet_type_uri}/topic/{id}")
096        @Override
097        public void updateFacet(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri,
098                                                 FacetValue value, @HeaderParam("Cookie") ClientState clientState) {
099            DeepaMehtaTransaction tx = dms.beginTx();
100            try {
101                updateFacet(dms.getTopic(topicId, false), facetTypeUri, value, clientState, new Directives());
102                //
103                tx.success();
104            } catch (Exception e) {
105                logger.warning("ROLLBACK!");
106                throw new RuntimeException("Updating facet \"" + facetTypeUri + "\" of topic " + topicId +
107                    " failed (value=" + value + ")", e);
108            } finally {
109                tx.finish();
110            }
111        }
112    
113        @Override
114        public void updateFacet(DeepaMehtaObject object, String facetTypeUri, FacetValue value,
115                                                         ClientState clientState, Directives directives) {
116            AssociationDefinition assocDef = getAssocDef(facetTypeUri);
117            if (!isMultiFacet(facetTypeUri)) {
118                object.updateChildTopic(value.getTopic(), assocDef, clientState, directives);
119            } else {
120                object.updateChildTopics(value.getTopics(), assocDef, clientState, directives);
121            }
122        }
123    
124        // ---
125    
126        // Note: there is a similar private method in AttachedDeepaMehtaObject:
127        // fetchChildTopic(AssociationDefinition assocDef, long childTopicId, boolean fetchComposite)
128        // ### TODO: Extend DeepaMehtaObject interface by hasChildTopic()?
129        @Override
130        public boolean hasFacet(long topicId, String facetTypeUri, long facetTopicId) {
131            String assocTypeUri = getAssocDef(facetTypeUri).getInstanceLevelAssocTypeUri();
132            Association assoc = dms.getAssociation(assocTypeUri, topicId, facetTopicId, "dm4.core.parent", "dm4.core.child",
133                false);     // fetchComposite=false
134            return assoc != null;
135        }
136    
137    
138    
139        // ------------------------------------------------------------------------------------------------- Private Methods
140    
141        /**
142         * Fetches and returns a child topic or <code>null</code> if no such topic extists.
143         * <p>
144         * Note: There is a principal copy in AttachedDeepaMehtaObject but here the precondition is different:
145         * The given association definition must not necessarily originate from the given object's type definition.
146         * ### TODO: meanwhile we have the ValueStorage. Can we use its method instead?
147         */
148        private RelatedTopic fetchChildTopic(DeepaMehtaObject object, AssociationDefinition assocDef,
149                                                                      boolean fetchComposite) {
150            String assocTypeUri  = assocDef.getInstanceLevelAssocTypeUri();
151            String othersTypeUri = assocDef.getChildTypeUri();
152            return object.getRelatedTopic(assocTypeUri, "dm4.core.parent", "dm4.core.child", othersTypeUri, fetchComposite,
153                false);
154        }
155    
156        /**
157         * Fetches and returns child topics.
158         * <p>
159         * Note: There is a principal copy in AttachedDeepaMehtaObject but here the precondition is different:
160         * The given association definition must not necessarily originate from the given object's type definition.
161         * ### TODO: meanwhile we have the ValueStorage. Can we use its method instead?
162         */
163        private List<RelatedTopic> fetchChildTopics(DeepaMehtaObject object, AssociationDefinition assocDef,
164                                                                             boolean fetchComposite) {
165            String assocTypeUri  = assocDef.getInstanceLevelAssocTypeUri();
166            String othersTypeUri = assocDef.getChildTypeUri();
167            return object.getRelatedTopics(assocTypeUri, "dm4.core.parent", "dm4.core.child", othersTypeUri, fetchComposite,
168                false, 0).getItems();
169        }
170    
171        // ---
172    
173        private boolean isMultiFacet(String facetTypeUri) {
174            return getAssocDef(facetTypeUri).getChildCardinalityUri().equals("dm4.core.many");
175        }
176    
177        private AssociationDefinition getAssocDef(String facetTypeUri) {
178            // Note: a facet type has exactly *one* association definition
179            return dms.getTopicType(facetTypeUri).getAssocDefs().iterator().next();
180        }
181    }