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