001package net.abriraqui.dm4.importexport; 002 003import com.sun.jersey.core.util.Base64; 004import de.deepamehta.core.Topic; 005import de.deepamehta.core.TopicType; 006import de.deepamehta.core.osgi.PluginActivator; 007import de.deepamehta.core.service.Inject; 008import de.deepamehta.core.service.Transactional; 009import de.deepamehta.plugins.files.FilesPlugin; 010import de.deepamehta.plugins.files.FilesService; 011import de.deepamehta.plugins.topicmaps.TopicmapsService; 012import de.deepamehta.plugins.topicmaps.model.AssociationViewmodel; 013import de.deepamehta.plugins.topicmaps.model.TopicViewmodel; 014import de.deepamehta.plugins.topicmaps.model.TopicmapViewmodel; 015 016import javax.ws.rs.CookieParam; 017import javax.ws.rs.POST; 018import javax.ws.rs.Path; 019import javax.ws.rs.Produces; 020import javax.xml.stream.XMLStreamException; 021import java.io.ByteArrayInputStream; 022import java.io.ByteArrayOutputStream; 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.File; 026import java.util.logging.Logger; 027 028@Path("/import-export") 029@Produces("application/json") 030public class ImportExportPlugin extends PluginActivator { 031 032 @Inject 033 private TopicmapsService topicmapsService; 034 @Inject 035 private FilesService filesService; 036 037 private Logger log = Logger.getLogger(getClass().getName()); 038 039 // Service implementation // 040 041 @POST 042 @Transactional 043 @Path("/export/json") 044 public Topic exportTopicmapToJSON(@CookieParam("dm4_topicmap_id") long topicmapId) { 045 try { 046 log.info("Exporting Topicmap JSON ######### " + topicmapId); 047 TopicmapViewmodel topicmap = topicmapsService.getTopicmap(topicmapId, true); 048 String json = topicmap.toJSON().toString(); 049 InputStream in = new ByteArrayInputStream(json.getBytes("UTF-8")); 050 String jsonFileName = "topicmap-" + topicmapId + ".txt"; 051 return filesService.createFile(in, prefix() + "/" + jsonFileName); 052 // return filesService.createFile(in, jsonFileName); 053 } catch (Exception e) { 054 throw new RuntimeException("Export failed", e); 055 } 056 } 057 058 @POST 059 @Path("/export/svg") 060 @Transactional 061 public String exportTopicmapToSVG(@CookieParam("dm4_topicmap_id") long topicmapId) throws XMLStreamException { 062 final int BOX_HEIGHT = 20; 063 final int MARGIN_LEFT = 5; 064 final int MARGIN_TOP = 14; 065 final int ICON_WIDTH = 16; 066 final int ICON_HEIGHT = 16; 067 try { 068 log.info("Exporting Topicmap SVG ######### " + topicmapId); 069 // 0) Fetch topicmap data 070 TopicmapViewmodel topicmap = topicmapsService.getTopicmap(topicmapId, true); 071 Iterable<TopicViewmodel> topics = topicmap.getTopics(); 072 Iterable<AssociationViewmodel> associations = topicmap.getAssociations(); 073 // 1) Setup default file name of SVG to write to 074 String svgFileName = "Exported_Topicmap_" + topicmapId + ".svg"; 075 // 2) Get DM4 filerepo configuration setting and write to document to root folder 076 String documentPath = filesService.getFile("/") + "/" + svgFileName; 077 // 3) Create SVGWriter 078 SVGRenderer svg = new SVGRenderer(documentPath); 079 svg.startGroupElement(topicmapId); 080 // 4) Create all associations 081 for (AssociationViewmodel association : associations) { 082 String valueAssoc = association.getSimpleValue().toString(); 083 long topic1Id = association.getRoleModel1().getPlayerId(); 084 long topic2Id = association.getRoleModel2().getPlayerId(); 085 TopicViewmodel topic1 = topicmap.getTopic(topic1Id); 086 int x1 = topic1.getX(); 087 int y1 = topic1.getY(); 088 TopicViewmodel topic2 = topicmap.getTopic(topic2Id); 089 int x2 = topic2.getX(); 090 int y2 = topic2.getY(); 091 // 092 int dx = x2 - x1; 093 int dy = y2 - y1; 094 int label_x = dx / 2; 095 int label_y = dy / 2; 096 double assocLine = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); 097 double alpha = Math.asin(dy / assocLine) * 180 / Math.PI; 098 if (dx < 0) { 099 alpha = -alpha; 100 } 101 svg.startGroupElement(association.getId()); 102 svg.line(x1, x2, y1, y2); 103 svg.text(label_x, label_y, x1 + 10, y1 + 10, valueAssoc, "grey", alpha); 104 svg.endElement(); 105 } 106 // 5) Create all topics 107 for (TopicViewmodel topic : topics) { 108 String value = topic.getSimpleValue().toString(); 109 int x = topic.getX(); 110 int y = topic.getY(); 111 boolean visibility = topic.getVisibility(); 112 int boxWidth = value.length() * 9; 113 if (!visibility) { 114 continue; 115 } 116 svg.startGroupElement(topic.getId()); 117 svg.rectangle(x - boxWidth / 2, y - BOX_HEIGHT / 2, boxWidth, BOX_HEIGHT, color(topic.getTypeUri())); 118 svg.text(x - boxWidth / 2 + MARGIN_LEFT, y - BOX_HEIGHT / 2 + MARGIN_TOP, value, "black"); 119 svg.image(x + boxWidth / 2, y, ICON_WIDTH, ICON_HEIGHT, typeIconDataUri(topic.getTypeUri())); 120 svg.endElement(); 121 } 122 // 6) Close SVGWriter 123 svg.endElement(); 124 svg.closeDocument(); 125 // 7) Create file topic for our new document 126 filesService.getFileTopic(prefix() + "/" + svgFileName); 127 // ### return fileTopic to webclient.. 128 return "{\"filepath\": \"" + documentPath + "\"}"; // render in OK Dialog where the file was written to 129 } catch (Exception e) { 130 throw new RuntimeException("Export Topicmap to SVG failed", e); 131 } 132 } 133 134 /** @POST 135 @Path("/import") 136 @Transactional 137 @Consumes("multipart/form-data") 138 public Topic importTopicmap(UploadedFile file) { 139 try { 140 String json = file.getString(); 141 142 JSONObject topicmap = new JSONObject(json); 143 JSONObject info = topicmap.getJSONObject("info"); 144 145 JSONArray assocsArray = topicmap.getJSONArray("assocs"); 146 JSONArray topicsArray = topicmap.getJSONArray("topics"); 147 148 String origTopicmapName = info.getString("value"); 149 Topic importedTopicmap = 150 topicmapsService.createTopicmap("Imported Topicmap: " + origTopicmapName 151 , "dm4.webclient.default_topicmap_renderer"); 152 long topicmapId = importedTopicmap.getId(); 153 log.info("###### importedTopicmapId " + topicmapId); 154 // 155 Map<Long, Long> mapTopicIds = new HashMap(); 156 importTopics(topicsArray, mapTopicIds, topicmapId); 157 importAssociations(assocsArray, mapTopicIds, topicmapId); 158 return importedTopicmap; 159 } catch (Exception e) { 160 throw new RuntimeException("Importing Topicmap FAILED", e); 161 } 162 } 163 164 // Import topics 165 private void importTopics(JSONArray topicsArray, Map<Long, Long> mapTopicIds, long topicmapId) { 166 for (int i = 0, size = topicsArray.length(); i < size; i++) { 167 try { 168 JSONObject topic = topicsArray.getJSONObject(i); 169 createTopic(topic, mapTopicIds, topicmapId); 170 } catch (Exception e) { 171 log.warning("Topic NOT imported!!" + e); 172 } 173 } 174 } 175 176 // Import associations 177 private void importAssociations(JSONArray assocsArray, Map<Long, Long> mapTopicIds, long topicmapId) { 178 for (int i = 0, size = assocsArray.length(); i < size; i++) { 179 try { 180 JSONObject association = assocsArray.getJSONObject(i); 181 createAssociation(association, mapTopicIds, topicmapId); 182 } catch (Exception e) { 183 log.warning("Association NOT imported"); 184 } 185 } 186 } **/ 187 188 private String color(String typeUri) { 189 if (typeUri.equals("dm4.contacts.institution")) { 190 return "lightblue"; 191 } else if (typeUri.equals("dm4.contacts.person")) { 192 return "lightblue"; 193 } else if (typeUri.equals("dm4.notes.note")) { 194 return "lightblue"; 195 } else { 196 return "lightblue"; 197 } 198 } 199 200 /** ### Make this work for custom icons too, this works currently just with icons included in the standard 201 * distribution. */ 202 private String typeIconDataUri(String typeUri) throws IOException { 203 TopicType topicType = dms.getTopicType(typeUri); 204 String iconPath = (String) topicType.getViewConfig("dm4.webclient.view_config", "dm4.webclient.icon"); 205 int sep = iconPath.indexOf("/", 2); 206 // String pluginPath = iconPath.substring(1, sep); 207 // Plugin plugin = dms.getPlugin(pluginPath); 208 String imagePath = "web" + iconPath.substring(sep); 209 InputStream iconIS = null; 210 try { 211 iconIS = getStaticResource(imagePath); 212 log.fine("##### IconIS " + iconIS); 213 } catch (Exception e) { 214 // Icon resource not found in this plugin 215 log.info("### FALLBACK to standard grey icon as typeIcon for \"" 216 + imagePath + "\"can not be determined " + "during SVG Export"); 217 iconIS = dms.getPlugin("de.deepamehta.webclient").getStaticResource("web/images/ball-gray.png"); 218 } 219 // create base64 representation of the current type icon 220 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 221 byte[] buffer = new byte[1024]; 222 int count = 0; 223 while ((count = iconIS.read(buffer)) != -1) { 224 baos.write(buffer, 0, count); 225 } 226 byte[] fileContent = baos.toByteArray(); 227 // all chars in encoded are guaranteed to be 7-bit ASCII 228 byte[] encoded = Base64.encode(fileContent); 229 String imgBase64Str = new String(encoded); 230 log.fine("##### IMG BASE64 " + imgBase64Str); 231 // 232 return "data:image/png;base64," + imgBase64Str; 233 } 234 235 /** 236 private void createTopic(JSONObject topic, Map<Long, Long> mapTopicIds, long topicmapId) throws JSONException { 237 TopicModel model = new TopicModel(topic); 238 ViewProperties viewProps = new ViewProperties(topic.getJSONObject("view_props")); 239 long origTopicId = model.getId(); 240 Topic newTopic = dms.createTopic(model); 241 long topicId = newTopic.getId(); 242 mapTopicIds.put(origTopicId, topicId); 243 topicmapsService.addTopicToTopicmap(topicmapId, topicId, viewProps); 244 } 245 246 private void createAssociation(JSONObject association, Map<Long, Long> mapTopicIds, long topicmapId) { 247 AssociationModel assocModel = new AssociationModel(association); 248 RoleModel role1 = assocModel.getRoleModel1(); 249 role1.setPlayerId(mapTopicIds.get(role1.getPlayerId())); 250 RoleModel role2 = assocModel.getRoleModel2(); 251 role2.setPlayerId(mapTopicIds.get(role2.getPlayerId())); 252 Association newAssociation = dms.createAssociation(assocModel); 253 long assocId = newAssociation.getId(); 254 topicmapsService.addAssociationToTopicmap(topicmapId, assocId); 255 } **/ 256 257 private String prefix() { 258 File repo = filesService.getFile("/"); 259 return ((FilesPlugin) filesService).repoPath(repo); 260 } 261 262}