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.CompositeValueModel; 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.ClientState; 017 import de.deepamehta.core.service.Directives; 018 import de.deepamehta.core.storage.spi.DeepaMehtaTransaction; 019 import de.deepamehta.core.util.DeepaMehtaUtils; 020 021 import javax.ws.rs.GET; 022 import javax.ws.rs.POST; 023 import javax.ws.rs.PUT; 024 import javax.ws.rs.HeaderParam; 025 import javax.ws.rs.Path; 026 import javax.ws.rs.PathParam; 027 import javax.ws.rs.QueryParam; 028 import javax.ws.rs.Produces; 029 import javax.ws.rs.Consumes; 030 031 import java.util.List; 032 import java.util.logging.Logger; 033 034 035 036 @Path("/facet") 037 @Consumes("application/json") 038 @Produces("application/json") 039 public class FacetsPlugin extends PluginActivator implements FacetsService { 040 041 // ---------------------------------------------------------------------------------------------- Instance Variables 042 043 private Logger logger = Logger.getLogger(getClass().getName()); 044 045 // -------------------------------------------------------------------------------------------------- Public Methods 046 047 048 049 // ************************************ 050 // *** FacetsService Implementation *** 051 // ************************************ 052 053 054 055 @GET 056 @Path("/{facet_type_uri}/topic/{id}") 057 @Override 058 public Topic getFacet(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri) { 059 return getFacet(dms.getTopic(topicId, false), facetTypeUri); // fetchComposite=false 060 } 061 062 @Override 063 public Topic getFacet(DeepaMehtaObject object, String facetTypeUri) { 064 // ### TODO: integrity check: is the object an instance of that facet type? 065 return fetchChildTopic(object, getAssocDef(facetTypeUri), true); // fetchComposite=true 066 } 067 068 // --- 069 070 @GET 071 @Path("/multi/{facet_type_uri}/topic/{id}") 072 @Override 073 public List<RelatedTopic> getFacets(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri) 074 { 075 return getFacets(dms.getTopic(topicId, false), facetTypeUri); // fetchComposite=false 076 } 077 078 @Override 079 public List<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), true); // fetchComposite=true 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, true); 092 CompositeValueModel comp = topic.getCompositeValue().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 comp.put(childTypeUri, value.getModel()); 099 } 100 } else { 101 List<RelatedTopic> values = getFacets(topic, facetTypeUri); 102 comp.put(childTypeUri, DeepaMehtaUtils.toTopicModels(values)); 103 } 104 } 105 return topic; 106 } 107 108 @POST 109 @Path("/{facet_type_uri}/topic/{id}") 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")), null); // clientState=null 115 } 116 117 // --- 118 119 @PUT 120 @Path("/{facet_type_uri}/topic/{id}") 121 @Override 122 public void updateFacet(@PathParam("id") long topicId, @PathParam("facet_type_uri") String facetTypeUri, 123 FacetValue value, @HeaderParam("Cookie") ClientState clientState) { 124 DeepaMehtaTransaction tx = dms.beginTx(); 125 try { 126 updateFacet(dms.getTopic(topicId, false), facetTypeUri, value, clientState, new Directives()); 127 // 128 tx.success(); 129 } catch (Exception e) { 130 logger.warning("ROLLBACK!"); 131 throw new RuntimeException("Updating facet \"" + facetTypeUri + "\" of topic " + topicId + 132 " failed (value=" + value + ")", e); 133 } finally { 134 tx.finish(); 135 } 136 } 137 138 @Override 139 public void updateFacet(DeepaMehtaObject object, String facetTypeUri, FacetValue value, 140 ClientState clientState, Directives directives) { 141 AssociationDefinition assocDef = getAssocDef(facetTypeUri); 142 if (!isMultiFacet(facetTypeUri)) { 143 object.updateChildTopic(value.getTopic(), assocDef, clientState, directives); 144 } else { 145 object.updateChildTopics(value.getTopics(), assocDef, clientState, directives); 146 } 147 } 148 149 // --- 150 151 // Note: there is a similar private method in AttachedDeepaMehtaObject: 152 // fetchChildTopic(AssociationDefinition assocDef, long childTopicId, boolean fetchComposite) 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 = dms.getAssociation(assocTypeUri, topicId, facetTopicId, "dm4.core.parent", "dm4.core.child", 158 false); // fetchComposite=false 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 AttachedDeepaMehtaObject 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 boolean fetchComposite) { 175 String assocTypeUri = assocDef.getInstanceLevelAssocTypeUri(); 176 String othersTypeUri = assocDef.getChildTypeUri(); 177 return object.getRelatedTopic(assocTypeUri, "dm4.core.parent", "dm4.core.child", othersTypeUri, fetchComposite, 178 false); 179 } 180 181 /** 182 * Fetches and returns child topics. 183 * <p> 184 * Note: There is a principal copy in AttachedDeepaMehtaObject but here the precondition is different: 185 * The given association definition must not necessarily originate from the given object's type definition. 186 * ### TODO: meanwhile we have the ValueStorage. Can we use its method instead? 187 */ 188 private List<RelatedTopic> fetchChildTopics(DeepaMehtaObject object, AssociationDefinition assocDef, 189 boolean fetchComposite) { 190 String assocTypeUri = assocDef.getInstanceLevelAssocTypeUri(); 191 String othersTypeUri = assocDef.getChildTypeUri(); 192 return object.getRelatedTopics(assocTypeUri, "dm4.core.parent", "dm4.core.child", othersTypeUri, fetchComposite, 193 false, 0).getItems(); 194 } 195 196 // --- 197 198 private boolean isMultiFacet(String facetTypeUri) { 199 return getAssocDef(facetTypeUri).getChildCardinalityUri().equals("dm4.core.many"); 200 } 201 202 private String getChildTypeUri(String facetTypeUri) { 203 return getAssocDef(facetTypeUri).getChildTypeUri(); 204 } 205 206 private AssociationDefinition getAssocDef(String facetTypeUri) { 207 // Note: a facet type has exactly *one* association definition 208 return dms.getTopicType(facetTypeUri).getAssocDefs().iterator().next(); 209 } 210 }