package de.deepamehta.facets;
import de.deepamehta.core.Association;
import de.deepamehta.core.AssociationDefinition;
import de.deepamehta.core.DeepaMehtaObject;
import de.deepamehta.core.RelatedTopic;
import de.deepamehta.core.Topic;
import de.deepamehta.core.model.ChildTopicsModel;
import de.deepamehta.core.model.RelatedTopicModel;
import de.deepamehta.core.model.facets.FacetValueModel;
import de.deepamehta.core.osgi.PluginActivator;
import de.deepamehta.core.service.Transactional;
import de.deepamehta.core.util.DeepaMehtaUtils;
import org.codehaus.jettison.json.JSONObject;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import java.util.List;
import java.util.logging.Logger;
// ### TODO: support custom assoc types also for facets.
// Some assocDef.getChildTypeUri() calls must be replaced by assocDef.getAssocDefUri().
@Path("/facet")
@Consumes("application/json")
@Produces("application/json")
public class FacetsPlugin extends PluginActivator implements FacetsService {
// ---------------------------------------------------------------------------------------------- Instance Variables
private Logger logger = Logger.getLogger(getClass().getName());
// -------------------------------------------------------------------------------------------------- Public Methods
// ************************************
// *** FacetsService Implementation ***
// ************************************
@GET
@Path("/{facet_type_uri}/topic/{id}")
@Override
public RelatedTopic getFacet(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri) {
return getFacet(dm4.getTopic(topicId), facetTypeUri);
}
@Override
public RelatedTopic getFacet(DeepaMehtaObject object, String facetTypeUri) {
// ### TODO: integrity check: is the object an instance of that facet type?
return fetchChildTopic(object, getAssocDef(facetTypeUri));
}
// ---
@GET
@Path("/multi/{facet_type_uri}/topic/{id}")
@Override
public List<RelatedTopic> getFacets(@PathParam("id") long topicId,
@PathParam("facet_type_uri") String facetTypeUri) {
return getFacets(dm4.getTopic(topicId), facetTypeUri);
}
@Override
public List<RelatedTopic> getFacets(DeepaMehtaObject object, String facetTypeUri) {
// ### TODO: integrity check: is the object an instance of that facet type?
return fetchChildTopics(object, getAssocDef(facetTypeUri));
}
// ---
@GET
@Path("/topic/{id}")
@Override
public Topic getFacettedTopic(@PathParam("id") long topicId,
@QueryParam("facet_type_uri") List<String> facetTypeUris) {
try {
Topic topic = dm4.getTopic(topicId);
ChildTopicsModel childTopics = topic.getChildTopics().getModel();
for (String facetTypeUri : facetTypeUris) {
String childTypeUri = getChildTypeUri(facetTypeUri);
if (!isMultiFacet(facetTypeUri)) {
Topic value = getFacet(topic, facetTypeUri);
if (value != null) {
childTopics.put(childTypeUri, value.getModel());
}
} else {
List<RelatedTopic> values = getFacets(topic, facetTypeUri);
// Note: without the type witness the generic put() method (which takes an Object) would be called
childTopics.put(childTypeUri, DeepaMehtaUtils.<RelatedTopicModel>toModelList(values));
}
}
return topic;
} catch (Exception e) {
throw new RuntimeException("Getting facetted topic " + topicId + " failed (facetTypeUris=" + facetTypeUris +
")", e);
}
}
@POST
@Path("/{facet_type_uri}/topic/{id}")
@Transactional
@Override
public void addFacetTypeToTopic(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri) {
dm4.createAssociation(mf.newAssociationModel("dm4.core.instantiation",
mf.newTopicRoleModel(topicId, "dm4.core.instance"),
mf.newTopicRoleModel(facetTypeUri, "dm4.facets.facet")
));
}
// ---
@PUT
@Path("/{facet_type_uri}/topic/{id}")
@Transactional
@Override
public void updateFacet(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri,
FacetValueModel value) {
try {
updateFacet(dm4.getTopic(topicId), facetTypeUri, value);
} catch (Exception e) {
throw new RuntimeException("Updating facet \"" + facetTypeUri + "\" of topic " + topicId +
" failed (value=" + value + ")", e);
}
}
@Override
public void updateFacet(DeepaMehtaObject object, String facetTypeUri, FacetValueModel value) {
AssociationDefinition assocDef = getAssocDef(facetTypeUri);
if (!isMultiFacet(facetTypeUri)) {
object.updateChildTopic(value.getTopic(), assocDef);
} else {
object.updateChildTopics(value.getTopics(), assocDef);
}
}
// ---
// Note: there is a similar private method in DeepaMehtaObjectImpl:
// fetchChildTopic(AssociationDefinition assocDef, long childTopicId)
// ### TODO: Extend DeepaMehtaObject interface by hasChildTopic()?
@Override
public boolean hasFacet(long topicId, String facetTypeUri, long facetTopicId) {
String assocTypeUri = getAssocDef(facetTypeUri).getInstanceLevelAssocTypeUri();
Association assoc = dm4.getAssociation(assocTypeUri, topicId, facetTopicId,
"dm4.core.parent", "dm4.core.child");
return assoc != null;
}
// ------------------------------------------------------------------------------------------------- Private Methods
/**
* Fetches and returns a child topic or <code>null</code> if no such topic extists.
* <p>
* Note: There is a principal copy in DeepaMehtaObjectImpl but here the precondition is different:
* The given association definition must not necessarily originate from the given object's type definition.
* ### TODO: meanwhile we have the ValueStorage. Can we use its method instead?
*/
private RelatedTopic fetchChildTopic(DeepaMehtaObject object, AssociationDefinition assocDef) {
String assocTypeUri = assocDef.getInstanceLevelAssocTypeUri();
String othersTypeUri = assocDef.getChildTypeUri();
return object.getRelatedTopic(assocTypeUri, "dm4.core.parent", "dm4.core.child", othersTypeUri);
}
/**
* Fetches and returns child topics.
* <p>
* Note: There is a principal copy in DeepaMehtaObjectImpl but here the precondition is different:
* The given association definition must not necessarily originate from the given object's type definition.
* ### TODO: meanwhile we have the ValueStorage. Can we use its method instead?
*/
private List<RelatedTopic> fetchChildTopics(DeepaMehtaObject object, AssociationDefinition assocDef) {
String assocTypeUri = assocDef.getInstanceLevelAssocTypeUri();
String othersTypeUri = assocDef.getChildTypeUri();
return object.getRelatedTopics(assocTypeUri, "dm4.core.parent", "dm4.core.child", othersTypeUri);
}
// ---
private boolean isMultiFacet(String facetTypeUri) {
return getAssocDef(facetTypeUri).getChildCardinalityUri().equals("dm4.core.many");
}
private String getChildTypeUri(String facetTypeUri) {
return getAssocDef(facetTypeUri).getChildTypeUri();
}
private AssociationDefinition getAssocDef(String facetTypeUri) {
// Note: a facet type has exactly *one* association definition
return dm4.getTopicType(facetTypeUri).getAssocDefs().iterator().next();
}
}