/* * Copyright 2015 Igor Maznitsa. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.igormaznitsa.mindmap.plugins.exporters; import com.igormaznitsa.mindmap.plugins.api.AbstractExporter; import com.igormaznitsa.mindmap.model.Extra; import com.igormaznitsa.mindmap.model.ExtraFile; import com.igormaznitsa.mindmap.model.ExtraLink; import com.igormaznitsa.mindmap.model.ExtraLinkable; import com.igormaznitsa.mindmap.model.ExtraNote; import com.igormaznitsa.mindmap.model.ExtraTopic; import com.igormaznitsa.mindmap.model.Topic; import com.igormaznitsa.mindmap.swing.panel.MindMapPanel; import com.igormaznitsa.mindmap.swing.panel.MindMapPanelConfig; import com.igormaznitsa.mindmap.swing.panel.ui.AbstractCollapsableElement; import com.igormaznitsa.mindmap.swing.panel.utils.Utils; import java.awt.Color; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.swing.JComponent; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringEscapeUtils; import com.igormaznitsa.mindmap.swing.panel.utils.MindMapUtils; import com.igormaznitsa.mindmap.swing.panel.Texts; import com.igormaznitsa.mindmap.swing.services.IconID; import com.igormaznitsa.mindmap.swing.services.ImageIconServiceProvider; import com.igormaznitsa.meta.annotation.MustNotContainNull; import javax.swing.Icon; import static com.igormaznitsa.meta.common.utils.Assertions.assertNotNull; public class FreeMindExporter extends AbstractExporter { private static class State { private static final String NEXT_LINE = "\r\n";//NOI18N private final StringBuilder buffer = new StringBuilder(16384); @Nonnull public State append(final char ch) { this.buffer.append(ch); return this; } @Nonnull public State append(final long val) { this.buffer.append(val); return this; } @Nonnull public State append(final @Nonnull String str) { this.buffer.append(str); return this; } @Nonnull public State nextLine() { this.buffer.append(NEXT_LINE); return this; } @Override @Nonnull public String toString() { return this.buffer.toString(); } } private static final Icon ICO = ImageIconServiceProvider.findInstance().getIconForId(IconID.POPUP_EXPORT_FREEMIND); @Nonnull private static String generateString(final char chr, final int length) { final StringBuilder buffer = new StringBuilder(length); for (int i = 0; i < length; i++) { buffer.append(chr); } return buffer.toString(); } @Nonnull private static String makeUID(@Nonnull final Topic t) { final int[] path = t.getPositionPath(); final StringBuilder buffer = new StringBuilder("mmlink");//NOI18N for (final int i : path) { buffer.append('A' + i); } return buffer.toString(); } private static void writeTopicRecursively(@Nonnull final Topic topic, @Nonnull final MindMapPanelConfig cfg, int shift, @Nonnull final State state) { final String mainShiftStr = generateString(' ', shift); final Color edge = cfg.getConnectorColor(); String position = topic.getTopicLevel() == 1 ? (AbstractCollapsableElement.isLeftSidedTopic(topic) ? "left" : "right") : ""; //NOI18N state.append(mainShiftStr) .append("<node CREATED=\"") //NOI18N .append(System.currentTimeMillis()) //NOI18N .append("\" MODIFIED=\"") //NOI18N .append(System.currentTimeMillis()) //NOI18N .append("\" COLOR=\"") //NOI18N .append(assertNotNull(Utils.color2html(MindMapUtils.getTextColor(cfg, topic),false))) //NOI18N .append("\" BACKGROUND_COLOR=\"") //NOI18N .append(assertNotNull(Utils.color2html(MindMapUtils.getBackgroundColor(cfg, topic),false))) //NOI18N .append("\" ") //NOI18N .append(position.isEmpty() ? " " : String.format("POSITION=\"%s\"", position)) //NOI18N .append(" ID=\"") //NOI18N .append(makeUID(topic)) //NOI18N .append("\" ") //NOI18N .append("TEXT=\"") //NOI18N .append(escapeXML(topic.getText())) .append("\" "); //NOI18N final ExtraFile file = (ExtraFile) topic.getExtras().get(Extra.ExtraType.FILE); final ExtraLink link = (ExtraLink) topic.getExtras().get(Extra.ExtraType.LINK); final ExtraTopic transition = (ExtraTopic) topic.getExtras().get(Extra.ExtraType.TOPIC); final String thelink; final List<Extra<?>> extrasToSaveInText = new ArrayList<Extra<?>>(); // make some prioritization for only attribute if (transition != null) { thelink = '#' + makeUID(assertNotNull(topic.getMap().findTopicForLink(transition)));//NOI18N if (file != null) { extrasToSaveInText.add(file); } if (link != null) { extrasToSaveInText.add(link); } } else if (file != null) { thelink = file.getValue().toString(); if (link != null) { extrasToSaveInText.add(link); } } else if (link != null) { thelink = link.getValue().toString(); } else { thelink = "";//NOI18N } if (!thelink.isEmpty()) { state.append(" LINK=\"").append(escapeXML(thelink)).append("\"");//NOI18N } state.append(">").nextLine();//NOI18N shift++; final String childShift = generateString(' ', shift);//NOI18N state.append(childShift).append("<edge COLOR=\"").append(assertNotNull(Utils.color2html(edge,false))).append("\"/>").nextLine();//NOI18N final ExtraNote note = (ExtraNote) topic.getExtras().get(Extra.ExtraType.NOTE); final StringBuilder htmlTextForNode = new StringBuilder(); if (!extrasToSaveInText.isEmpty()) { htmlTextForNode.append("<ul>"); //NOI18N for (final Extra<?> e : extrasToSaveInText) { htmlTextForNode.append("<li>"); //NOI18N if (e instanceof ExtraLinkable) { final String linkAsText = ((ExtraLinkable) e).getAsURI().asString(true,e.getType() != Extra.ExtraType.FILE); htmlTextForNode.append("<b>").append(StringEscapeUtils.escapeHtml(e.getType().name())).append(": </b>").append("<a href=\"").append(linkAsText).append("\">").append(linkAsText).append("</a>"); //NOI18N } else { htmlTextForNode.append("<b>").append(StringEscapeUtils.escapeHtml(e.getType().name())).append(": </b>").append(StringEscapeUtils.escapeHtml(e.getAsString())); //NOI18N } htmlTextForNode.append("</li>"); //NOI18N } htmlTextForNode.append("</ul>"); //NOI18N } if (note != null) { htmlTextForNode.append("<p><pre>").append(StringEscapeUtils.escapeHtml(note.getValue())).append("</pre></p>"); //NOI18N } if (htmlTextForNode.length() > 0) { state.append(childShift).append("<richcontent TYPE=\"NOTE\">").append("<html><head></head><body>" + htmlTextForNode.toString() + "</body></html>").append("</richcontent>").nextLine();//NOI18N //NOI18N } for (final Topic ch : topic.getChildren()) { writeTopicRecursively(ch, cfg, shift, state); } state.append(mainShiftStr).append("</node>").nextLine();//NOI18N } @Override public void doExport(@Nonnull final MindMapPanel panel, @Nullable final JComponent options, @Nullable final OutputStream out) throws IOException { final State state = new State(); state.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>").nextLine();//NOI18N state.append("<!--").nextLine().append("Generated by NB Mind Map Plugin (https://github.com/raydac/netbeans-mmd-plugin)").nextLine();//NOI18N state.append(new Timestamp(new java.util.Date().getTime()).toString()).nextLine().append("-->").nextLine();//NOI18N state.append("<map version=\"1.0.1\" background_color=\"").append(assertNotNull(Utils.color2html(panel.getConfiguration().getPaperColor(),false))).append("\">").nextLine();//NOI18N final Topic root = panel.getModel().getRoot(); if (root != null) { writeTopicRecursively(root, panel.getConfiguration(), 1, state); } state.append("</map>");//NOI18N final String text = state.toString(); File fileToSaveMap = null; OutputStream theOut = out; if (theOut == null) { fileToSaveMap = MindMapUtils.selectFileToSaveForFileFilter(panel, Texts.getString("FreeMindExporter.saveDialogTitle"), ".mm", Texts.getString("FreeMindExporter.filterDescription"), Texts.getString("FreeMindExporter.approveButtonText")); fileToSaveMap = MindMapUtils.checkFileAndExtension(panel, fileToSaveMap, ".mm");//NOI18N theOut = fileToSaveMap == null ? null : new BufferedOutputStream(new FileOutputStream(fileToSaveMap, false)); } if (theOut != null) { try { IOUtils.write(text, theOut, "UTF-8"); } finally { if (fileToSaveMap != null) { IOUtils.closeQuietly(theOut); } } } if (fileToSaveMap != null) { FileUtils.writeStringToFile(fileToSaveMap, text, "UTF-8");//NOI18N } } @Override @Nullable public String getMnemonic() { return "freemind"; } @Nonnull private static String escapeXML(@Nonnull final String text) { return StringEscapeUtils.escapeXml(text).replace("\n", " "); //NOI18N } @Override @Nonnull public String getName(@Nonnull final MindMapPanel panel, @Nullable Topic actionTopic, @Nonnull @MustNotContainNull Topic[] selectedTopics) { return Texts.getString("FreeMindExporter.exporterName"); } @Override @Nonnull public String getReference(@Nonnull final MindMapPanel panel, @Nullable Topic actionTopic, @Nonnull @MustNotContainNull Topic[] selectedTopics) { return Texts.getString("FreeMindExporter.exporterReference"); } @Override @Nonnull public Icon getIcon(@Nonnull final MindMapPanel panel, @Nullable Topic actionTopic, @Nonnull @MustNotContainNull Topic[] selectedTopics) { return ICO; } @Override public int getOrder() { return 1; } }