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