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