/*
* 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.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.utils.Utils;
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.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.Icon;
import javax.swing.JComponent;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import com.igormaznitsa.meta.annotation.MustNotContainNull;
import com.igormaznitsa.mindmap.swing.panel.Texts;
import com.igormaznitsa.mindmap.swing.panel.utils.MindMapUtils;
import com.igormaznitsa.mindmap.swing.services.IconID;
import com.igormaznitsa.mindmap.swing.services.ImageIconServiceProvider;
public class TextExporter extends AbstractExporter {
private static final int SHIFT_STEP = 1;
private static final Icon ICO = ImageIconServiceProvider.findInstance().getIconForId(IconID.POPUP_EXPORT_TEXT);
private static class State {
private static final String NEXT_LINE = System.getProperty("line.separator", "\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(@Nonnull final 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();
}
}
@Nonnull
@MustNotContainNull
private static String[] split(@Nonnull final String text) {
return text.replace("\r", "").split("\\n");//NOI18N
}
@Nonnull
private static String replaceAllNextLineSeq(@Nonnull final String text, @Nonnull final String newNextLine) {
return text.replace("\r", "").replace("\n", newNextLine);//NOI18N
}
@Nonnull
private static String shiftString(@Nonnull final String text, final char fill, final int shift) {
final String[] lines = split(text);
final StringBuilder builder = new StringBuilder();
final String line = generateString(fill, shift);
boolean nofirst = false;
for (final String s : lines) {
if (nofirst) {
builder.append(State.NEXT_LINE);
}
else {
nofirst = true;
}
builder.append(line).append(s);
}
return builder.toString();
}
@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 makeLineFromString(@Nonnull final String text) {
final StringBuilder result = new StringBuilder(text.length());
for (final char c : text.toCharArray()) {
if (Character.isISOControl(c)) {
result.append(' ');
}
else {
result.append(c);
}
}
return result.toString();
}
private static int getMaxLineWidth(@Nonnull final String text) {
final String[] lines = replaceAllNextLineSeq(text, "\n").split("\\n");//NOI18N
int max = 0;
for (final String s : lines) {
max = Math.max(s.length(), max);
}
return max;
}
private static void writeTopic(@Nonnull final Topic topic, final char ch, final int shift, @Nonnull final State state) {
final int maxLen = getMaxLineWidth(topic.getText());
state.append(shiftString(topic.getText(), ' ', shift)).nextLine().append(shiftString(generateString(ch, maxLen + 2), ' ', shift)).nextLine();//NOI18N
final ExtraFile file = (ExtraFile) topic.getExtras().get(Extra.ExtraType.FILE);
final ExtraLink link = (ExtraLink) topic.getExtras().get(Extra.ExtraType.LINK);
final ExtraNote note = (ExtraNote) topic.getExtras().get(Extra.ExtraType.NOTE);
final ExtraTopic transition = (ExtraTopic) topic.getExtras().get(Extra.ExtraType.TOPIC);
boolean hasExtras = false;
boolean extrasPrinted = false;
if (file != null || link != null || note != null || transition != null) {
hasExtras = true;
}
if (file != null) {
final String uri = file.getValue().asString(false, false);
state.append(shiftString("FILE: ", ' ', shift)).append(uri).nextLine();//NOI18N
extrasPrinted = true;
}
if (link != null) {
final String uri = link.getValue().asString(false, false);
state.append(shiftString("URL: ", ' ', shift)).append(uri).nextLine();//NOI18N
extrasPrinted = true;
}
if (transition != null) {
final Topic linkedTopic = topic.getMap().findTopicForLink(transition);
state.append(shiftString("Related to: ", ' ', shift)).append(linkedTopic == null ? "<UNKNOWN>" : '\"' + makeLineFromString(linkedTopic.getText()) + "\"").nextLine();//NOI18N
extrasPrinted = true;
}
if (note != null) {
if (extrasPrinted) {
state.nextLine();
}
state.append(shiftString(note.getValue(), ' ', shift)).nextLine();
}
final Map<String,String> codeSnippets = topic.getCodeSnippets();
if (!codeSnippets.isEmpty()) {
boolean first = true;
for(final Map.Entry<String,String> e : codeSnippets.entrySet()) {
final String lang = e.getKey();
if (!first) {
state.nextLine();
} else {
first = false;
}
state.append(shiftString("====BEGIN SOURCE ("+lang+')',' ',shift)).nextLine();
final String body = e.getValue();
for(final String s : StringUtils.split(body,'\n')){
state.append(shiftString(Utils.removeAllISOControlsButTabs(s),' ',shift)).nextLine();
}
state.append(shiftString("====END SOURCE", ' ', shift)).nextLine();
}
}
}
private void writeInterTopicLine(@Nonnull final State state) {
state.nextLine();
}
private void writeOtherTopicRecursively(@Nonnull final Topic t, int shift, @Nonnull final State state) {
writeInterTopicLine(state);
writeTopic(t, '.', shift, state);
shift += SHIFT_STEP;
for (final Topic ch : t.getChildren()) {
writeOtherTopicRecursively(ch, shift, state);
}
}
@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("# Generated by NB Mind Map Plugin (https://github.com/raydac/netbeans-mmd-plugin)").nextLine();//NOI18N
state.append("# ").append(new Timestamp(new java.util.Date().getTime()).toString()).nextLine().nextLine();//NOI18N
int shift = 0;
final Topic root = panel.getModel().getRoot();
if (root != null) {
writeTopic(root, '=', shift, state);//NOI18N
shift += SHIFT_STEP;
final Topic[] children = Utils.getLeftToRightOrderedChildrens(root);
for (final Topic t : children) {
writeInterTopicLine(state);
writeTopic(t, '-', shift, state);
shift += SHIFT_STEP;
for (final Topic tt : t.getChildren()) {
writeOtherTopicRecursively(tt, shift, state);
}
shift -= SHIFT_STEP;
}
}
final String text = state.toString();
File fileToSaveMap = null;
OutputStream theOut = out;
if (theOut == null) {
fileToSaveMap = MindMapUtils.selectFileToSaveForFileFilter(panel, Texts.getString("TextExporter.saveDialogTitle"), ".txt", Texts.getString("TextExporter.filterDescription"), Texts.getString("TextExporter.approveButtonText"));
fileToSaveMap = MindMapUtils.checkFileAndExtension(panel, fileToSaveMap, ".txt");//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);
}
}
}
}
@Override
@Nullable
public String getMnemonic() {
return "text";
}
@Override
@Nonnull
public String getName(@Nonnull final MindMapPanel panel, @Nullable Topic actionTopic, @Nonnull @MustNotContainNull Topic[] selectedTopics) {
return Texts.getString("TextExporter.exporterName");
}
@Override
@Nonnull
public String getReference(@Nonnull final MindMapPanel panel, @Nullable Topic actionTopic, @Nonnull @MustNotContainNull Topic[] selectedTopics) {
return Texts.getString("TextExporter.exporterReference");
}
@Override
@Nonnull
public Icon getIcon(@Nonnull final MindMapPanel panel, @Nullable Topic actionTopic, @Nonnull @MustNotContainNull Topic[] selectedTopics) {
return ICO;
}
@Override
public int getOrder() {
return 6;
}
}