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}