001package de.deepamehta.facets;
002
003import de.deepamehta.core.Association;
004import de.deepamehta.core.AssociationDefinition;
005import de.deepamehta.core.DeepaMehtaObject;
006import de.deepamehta.core.RelatedTopic;
007import de.deepamehta.core.Topic;
008import de.deepamehta.core.model.ChildTopicsModel;
009import de.deepamehta.core.model.facets.FacetValueModel;
010import de.deepamehta.core.osgi.PluginActivator;
011import de.deepamehta.core.service.Transactional;
012import de.deepamehta.core.util.DeepaMehtaUtils;
013
014import org.codehaus.jettison.json.JSONObject;
015
016import javax.ws.rs.GET;
017import javax.ws.rs.POST;
018import javax.ws.rs.PUT;
019import javax.ws.rs.HeaderParam;
020import javax.ws.rs.Path;
021import javax.ws.rs.PathParam;
022import javax.ws.rs.QueryParam;
023import javax.ws.rs.Produces;
024import javax.ws.rs.Consumes;
025
026import java.util.List;
027import java.util.logging.Logger;
028
029
030
031// ### TODO: support custom assoc types also for facets.
032// Some assocDef.getChildTypeUri() calls must be replaced by assocDef.getAssocDefUri().
033@Path("/facet")
034@Consumes("application/json")
035@Produces("application/json")
036public 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(dm4.getTopic(topicId), facetTypeUri);
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));
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,
071                                        @PathParam("facet_type_uri") String facetTypeUri) {
072        return getFacets(dm4.getTopic(topicId), facetTypeUri);
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));
079    }
080
081    // ---
082
083    @GET
084    @Path("/topic/{id}")
085    @Override
086    public Topic getFacettedTopic(@PathParam("id") long topicId,
087                                  @QueryParam("facet_type_uri") List<String> facetTypeUris) {
088        Topic topic = dm4.getTopic(topicId);
089        ChildTopicsModel childTopics = topic.getChildTopics().getModel();
090        for (String facetTypeUri : facetTypeUris) {
091            String childTypeUri = getChildTypeUri(facetTypeUri);
092            if (!isMultiFacet(facetTypeUri)) {
093                Topic value = getFacet(topic, facetTypeUri);
094                if (value != null) {
095                    childTopics.put(childTypeUri, value.getModel());
096                }
097            } else {
098                List<RelatedTopic> values = getFacets(topic, facetTypeUri);
099                childTopics.put(childTypeUri, DeepaMehtaUtils.toTopicModels(values));
100            }
101        }
102        return topic;
103    }
104
105    @POST
106    @Path("/{facet_type_uri}/topic/{id}")
107    @Transactional
108    @Override
109    public void addFacetTypeToTopic(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri) {
110        dm4.createAssociation(mf.newAssociationModel("dm4.core.instantiation",
111            mf.newTopicRoleModel(topicId,      "dm4.core.instance"),
112            mf.newTopicRoleModel(facetTypeUri, "dm4.facets.facet")
113        ));
114    }
115
116    // ---
117
118    @PUT
119    @Path("/{facet_type_uri}/topic/{id}")
120    @Transactional
121    @Override
122    public void updateFacet(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri,
123                                                                                        FacetValueModel value) {
124        try {
125            updateFacet(dm4.getTopic(topicId), facetTypeUri, value);
126        } catch (Exception e) {
127            throw new RuntimeException("Updating facet \"" + facetTypeUri + "\" of topic " + topicId +
128                " failed (value=" + value + ")", e);
129        }
130    }
131
132    @Override
133    public void updateFacet(DeepaMehtaObject object, String facetTypeUri, FacetValueModel value) {
134        AssociationDefinition assocDef = getAssocDef(facetTypeUri);
135        if (!isMultiFacet(facetTypeUri)) {
136            object.updateChildTopic(value.getTopic(), assocDef);
137        } else {
138            object.updateChildTopics(value.getTopics(), assocDef);
139        }
140    }
141
142    // ---
143
144    // Note: there is a similar private method in DeepaMehtaObjectImpl:
145    // fetchChildTopic(AssociationDefinition assocDef, long childTopicId)
146    // ### TODO: Extend DeepaMehtaObject interface by hasChildTopic()?
147    @Override
148    public boolean hasFacet(long topicId, String facetTypeUri, long facetTopicId) {
149        String assocTypeUri = getAssocDef(facetTypeUri).getInstanceLevelAssocTypeUri();
150        Association assoc = dm4.getAssociation(assocTypeUri, topicId, facetTopicId,
151            "dm4.core.parent", "dm4.core.child");
152        return assoc != null;
153    }
154
155
156
157    // ------------------------------------------------------------------------------------------------- Private Methods
158
159    /**
160     * Fetches and returns a child topic or <code>null</code> if no such topic extists.
161     * <p>
162     * Note: There is a principal copy in DeepaMehtaObjectImpl but here the precondition is different:
163     * The given association definition must not necessarily originate from the given object's type definition.
164     * ### TODO: meanwhile we have the ValueStorage. Can we use its method instead?
165     */
166    private RelatedTopic fetchChildTopic(DeepaMehtaObject object, AssociationDefinition assocDef) {
167        String assocTypeUri  = assocDef.getInstanceLevelAssocTypeUri();
168        String othersTypeUri = assocDef.getChildTypeUri();
169        return object.getRelatedTopic(assocTypeUri, "dm4.core.parent", "dm4.core.child", othersTypeUri);
170    }
171
172    /**
173     * Fetches and returns child topics.
174     * <p>
175     * Note: There is a principal copy in DeepaMehtaObjectImpl but here the precondition is different:
176     * The given association definition must not necessarily originate from the given object's type definition.
177     * ### TODO: meanwhile we have the ValueStorage. Can we use its method instead?
178     */
179    private List<RelatedTopic> fetchChildTopics(DeepaMehtaObject object, AssociationDefinition assocDef) {
180        String assocTypeUri  = assocDef.getInstanceLevelAssocTypeUri();
181        String othersTypeUri = assocDef.getChildTypeUri();
182        return object.getRelatedTopics(assocTypeUri, "dm4.core.parent", "dm4.core.child", othersTypeUri);
183    }
184
185    // ---
186
187    private boolean isMultiFacet(String facetTypeUri) {
188        return getAssocDef(facetTypeUri).getChildCardinalityUri().equals("dm4.core.many");
189    }
190
191    private String getChildTypeUri(String facetTypeUri) {
192        return getAssocDef(facetTypeUri).getChildTypeUri();
193    }
194
195    private AssociationDefinition getAssocDef(String facetTypeUri) {
196        // Note: a facet type has exactly *one* association definition
197        return dm4.getTopicType(facetTypeUri).getAssocDefs().iterator().next();
198    }
199}