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