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}