/*
* Copyright 2016 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.importers;
import com.igormaznitsa.mindmap.plugins.api.AbstractImporter;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.Icon;
import org.apache.commons.io.FileUtils;
import com.igormaznitsa.meta.annotation.MustNotContainNull;
import com.igormaznitsa.mindmap.model.MindMap;
import com.igormaznitsa.mindmap.model.MindMapController;
import com.igormaznitsa.mindmap.model.Topic;
import com.igormaznitsa.mindmap.swing.panel.DialogProvider;
import com.igormaznitsa.mindmap.swing.panel.MindMapPanel;
import com.igormaznitsa.mindmap.swing.panel.Texts;
import com.igormaznitsa.mindmap.swing.services.IconID;
import com.igormaznitsa.mindmap.swing.services.ImageIconServiceProvider;
import java.util.ArrayList;
import com.igormaznitsa.mindmap.swing.panel.ui.AbstractCollapsableElement;
public class Text2MindMapImporter extends AbstractImporter {
private static final Icon ICO = ImageIconServiceProvider.findInstance().getIconForId(IconID.POPUP_IMPORT_TXT2MM);
private static final int TAB_POSITIONS = 16;
private static final class TopicData {
private final int offset;
private final Topic topic;
public TopicData(final int offset, @Nonnull final Topic topic) {
this.offset = offset;
this.topic = topic;
}
}
@Override
@Nullable
public MindMap doImport(@Nonnull final MindMapPanel panel, @Nonnull final DialogProvider dialogProvider, @Nullable final Topic actionTopic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) throws Exception {
final File file = this.selectFileForExtension(panel, Texts.getString("MMDImporters.Text2MindMap.openDialogTitle"), "txt", "text files (.TXT)", Texts.getString("MMDImporters.ApproveImport"));
MindMap result = null;
if (file != null) {
final List<String> lines = FileUtils.readLines(file);
result = makeFromLines(lines, panel.getModel().getController());
}
return result;
}
@Nonnull
MindMap makeFromLines(@Nonnull @MustNotContainNull final List<String> lines, @Nullable final MindMapController controller) {
final MindMap result = new MindMap(controller, false);
final Iterator<String> iterator = lines.iterator();
final List<TopicData> topicStack = new ArrayList<TopicData>();
while (true) {
final Topic topic = decodeLine(result, iterator, topicStack);
if (topic == null) {
break;
}
}
final Topic root = result.getRoot();
final int size = root == null ? 0 : root.getChildren().size();
if (root != null && size != 0) {
final List<Topic> topics = root.getChildren();
final int left = (topics.size() + 1) / 2;
for (int i = 0; i < left; i++) {
AbstractCollapsableElement.makeTopicLeftSided(topics.get(i), true);
}
}
return result;
}
@Nullable
private String nextNonEmptyString(@Nonnull final Iterator<String> iterator) {
String result = null;
while (iterator.hasNext()) {
result = iterator.next();
if (result.trim().isEmpty()) {
result = null;
} else {
break;
}
}
return result;
}
private int calcDataOffset(@Nonnull final String text) {
int result = 0;
for (final char c : text.toCharArray()) {
if (c == '\t') {
result += TAB_POSITIONS - (result % TAB_POSITIONS);
} else if (Character.isWhitespace(c)) {
result++;
} else {
break;
}
}
return result;
}
@Nullable
private Topic findPrevTopicForOffset(@Nonnull @MustNotContainNull final List<TopicData> topicStack, final int detectedOffset) {
for (final TopicData d : topicStack) {
if (d.offset < detectedOffset) {
return d.topic;
}
}
TopicData result = null;
if (!topicStack.isEmpty()) {
for (final TopicData d : topicStack) {
if (result == null) {
result = d;
} else if (result.offset > d.offset) {
result = d;
}
}
}
return result == null ? null : result.topic;
}
@Nullable
private Topic decodeLine(@Nonnull final MindMap map, @Nonnull final Iterator<String> lines, @Nonnull @MustNotContainNull final List<TopicData> topicStack) {
Topic result = null;
final String line = nextNonEmptyString(lines);
if (line != null) {
final int currentOffset = calcDataOffset(line);
final String trimmed = line.trim();
final Topic parentTopic = findPrevTopicForOffset(topicStack, currentOffset);
if (parentTopic == null) {
result = new Topic(map, null, trimmed);
map.setRoot(result, false);
} else {
result = new Topic(map, parentTopic, trimmed);
}
topicStack.add(0, new TopicData(currentOffset, result));
}
return result;
}
@Override
@Nullable
public String getMnemonic() {
return "tabtext";
}
@Override
@Nonnull
public String getName(@Nonnull final MindMapPanel panel, @Nullable final Topic actionTopic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) {
return Texts.getString("MMDImporters.Text2MindMap.Name");
}
@Override
@Nonnull
public String getReference(@Nonnull final MindMapPanel panel, @Nullable final Topic actionTopic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) {
return Texts.getString("MMDImporters.Text2MindMap.Reference");
}
@Override
@Nonnull
public Icon getIcon(@Nonnull final MindMapPanel panel, @Nullable final Topic actionTopic, @Nonnull @MustNotContainNull final Topic[] selectedTopics) {
return ICO;
}
@Override
public int getOrder() {
return 1;
}
}