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