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