/*
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and above are dual-licensed
* under the Eclipse Public License (EPL), which is available at
* http://www.eclipse.org/legal/epl-v10.html and the GNU Lesser General Public
* License (LGPL), which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors: XMind Ltd. - initial API and implementation
*/
package org.xmind.ui.internal.imports.mm;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.osgi.util.NLS;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xmind.core.Core;
import org.xmind.core.CoreException;
import org.xmind.core.IBoundary;
import org.xmind.core.IFileEntry;
import org.xmind.core.IHtmlNotesContent;
import org.xmind.core.IIdentifiable;
import org.xmind.core.IImage;
import org.xmind.core.IImageSpan;
import org.xmind.core.ILegend;
import org.xmind.core.INotes;
import org.xmind.core.INumbering;
import org.xmind.core.IParagraph;
import org.xmind.core.IPlainNotesContent;
import org.xmind.core.IRelationship;
import org.xmind.core.ISheet;
import org.xmind.core.ISpan;
import org.xmind.core.ISummary;
import org.xmind.core.ITitled;
import org.xmind.core.ITopic;
import org.xmind.core.ITopicExtension;
import org.xmind.core.ITopicExtensionElement;
import org.xmind.core.ITopicRange;
import org.xmind.core.IWorkbook;
import org.xmind.core.internal.dom.NumberUtils;
import org.xmind.core.internal.dom.StyleSheetImpl;
import org.xmind.core.io.DirectoryStorage;
import org.xmind.core.io.IInputSource;
import org.xmind.core.io.IStorage;
import org.xmind.core.io.ResourceMappingManager;
import org.xmind.core.io.mindmanager.MMConstants;
import org.xmind.core.io.mindmanager.MMResourceMappingManager;
import org.xmind.core.style.IStyle;
import org.xmind.core.style.IStyleSheet;
import org.xmind.core.style.IStyled;
import org.xmind.core.util.DOMUtils;
import org.xmind.core.util.FileUtils;
import org.xmind.core.util.HyperlinkUtils;
import org.xmind.ui.internal.MindMapUIPlugin;
import org.xmind.ui.internal.imports.ImportMessages;
import org.xmind.ui.internal.imports.ImporterUtils;
import org.xmind.ui.internal.protocols.FilePathParser;
import org.xmind.ui.io.MonitoredInputStream;
import org.xmind.ui.resources.ColorUtils;
import org.xmind.ui.resources.FontUtils;
import org.xmind.ui.style.StyleUtils;
import org.xmind.ui.style.Styles;
import org.xmind.ui.wizards.MindMapImporter;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class MindManagerImporter extends MindMapImporter
implements MMConstants, ErrorHandler {
private static final Pattern DATE_PATTERN = Pattern.compile(
"((\\d+)-(\\d{1,2})-(\\d{1,2}))T((\\d{1,2}):(\\d{1,2}):(\\d{1,2}))"); //$NON-NLS-1$
private static final String TRANSPARENT_VALUE = "0.00"; //$NON-NLS-1$
private static Pattern OID_PATTERN = null;
private class NotesImporter {
IParagraph currentParagraph = null;
// IBaseParagraph currentParagraph = null;
Stack<IStyle> styleStack = new Stack<IStyle>();
IHtmlNotesContent content;
public NotesImporter(IHtmlNotesContent content) {
this.content = content;
}
public void loadFrom(Element notesGroupEle, Element element)
throws InterruptedException {
checkInterrupted();
String tagName = DOMUtils.getLocalName(element.getTagName());
if ("br".equalsIgnoreCase(tagName)) { //$NON-NLS-1$
addParagraph();
return;
}
boolean isParagraph = "p".equalsIgnoreCase(tagName) //$NON-NLS-1$
|| "li".equalsIgnoreCase(tagName); //$NON-NLS-1$
IStyle style = pushStyle(element,
isParagraph ? IStyle.PARAGRAPH : IStyle.TEXT);
if (isParagraph) {
addParagraph();
} else if ("img".equalsIgnoreCase(tagName)) { //$NON-NLS-1$
addImage(notesGroupEle, element);
}
NodeList nl = element.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
short nodeType = node.getNodeType();
if (nodeType == Node.TEXT_NODE) {
addText(node.getTextContent());
} else if (nodeType == Node.ELEMENT_NODE) {
loadFrom(notesGroupEle, (Element) node);
}
}
popStyle(style);
}
private void addText(String text) {
if (text == null)
return;
text = text.trim();
if ("".equals(text)) //$NON-NLS-1$
return;
addSpan(content.createTextSpan(text));
}
private void addImage(Element notesGroupEle, Element imgEle)
throws InterruptedException {
Iterator<Element> it = children(notesGroupEle, "ap:NotesData"); //$NON-NLS-1$
while (it.hasNext()) {
Element notesDataEle = it.next();
String imgUri = att(notesDataEle, "ImageUri"); //$NON-NLS-1$
String sourceImg = att(imgEle, "src"); //$NON-NLS-1$
if (imgUri != null && sourceImg != null
&& imgUri.equals(sourceImg)) {
loadImageAtt(imgUri, loadUri(notesDataEle));
String srcUrl = loadUri(notesDataEle);
if (srcUrl != null) {
String entryPath = idMap.get(srcUrl);
if (entryPath != null) {
addSpan(content.createImageSpan(HyperlinkUtils
.toAttachmentPath(entryPath)));
}
}
}
}
}
private void addSpan(ISpan span) {
if (currentParagraph == null)
addParagraph();
currentParagraph.addSpan(span);
if (!(span instanceof IImageSpan)) {
registerStyle(span, Styles.FontFamily, Styles.FontSize,
Styles.TextColor, Styles.FontWeight,
Styles.TextDecoration, Styles.FontStyle);
}
}
private void addParagraph() {
currentParagraph = content.createParagraph();
// currentParagraph = content
// .createParagraph(IBaseParagraph.GENERAL_PARAGRAPH);
content.addParagraph(currentParagraph);
registerStyle(currentParagraph, Styles.TextAlign);
}
private void registerStyle(IStyled host, String... keys) {
for (IStyle style : styleStack) {
registerStyle(host, style, keys);
}
}
private void registerStyle(IStyled host, IStyle style, String... keys) {
for (String key : keys) {
String value = style.getProperty(key);
if (value != null) {
MindManagerImporter.this.registerStyle(host, key, value);
}
}
}
private IStyle pushStyle(Element ele, String type) {
IStyle style = getTempStyleSheet().createStyle(type);
receiveStyle(ele, style);
if (style.isEmpty()) {
style = null;
} else {
styleStack.push(style);
}
return style;
}
private void receiveStyle(Element ele, IStyle style) {
String align = parseAlign(att(ele, "align")); //$NON-NLS-1$
if (align != null) {
style.setProperty(Styles.TextAlign, align);
}
String name = ele.getTagName();
if ("b".equalsIgnoreCase(name)) { //$NON-NLS-1$
style.setProperty(Styles.FontWeight, Styles.FONT_WEIGHT_BOLD);
} else if ("i".equalsIgnoreCase(name)) { //$NON-NLS-1$
style.setProperty(Styles.FontStyle, Styles.FONT_STYLE_ITALIC);
} else if ("u".equalsIgnoreCase(name)) { //$NON-NLS-1$
style.setProperty(Styles.TextDecoration,
Styles.TEXT_DECORATION_UNDERLINE);
} else if ("s".equalsIgnoreCase(name)) { //$NON-NLS-1$
style.setProperty(Styles.TextDecoration,
Styles.TEXT_DECORATION_LINE_THROUGH);
} else if ("font".equalsIgnoreCase(name)) { //$NON-NLS-1$
String fontName = att(ele, "face"); //$NON-NLS-1$
String availableFontName = FontUtils
.getAAvailableFontNameFor(fontName);
fontName = availableFontName != null ? availableFontName
: fontName;
if (fontName != null) {
style.setProperty(Styles.FontFamily, fontName);
}
} else if ("span".equalsIgnoreCase(name)) { //$NON-NLS-1$
if ("font".equalsIgnoreCase( //$NON-NLS-1$
((Element) ele.getParentNode()).getTagName())) {
String color = att((Element) ele.getParentNode(), "color"); //$NON-NLS-1$
style.setProperty(Styles.TextColor, color);
}
}
String styleContent = att(ele, "style"); //$NON-NLS-1$
if (styleContent != null) {
receiveStyleContent(style, styleContent);
}
}
private String parseAlign(String align) {
if (align != null) {
if (Styles.ALIGN_CENTER.equalsIgnoreCase(align) //
|| Styles.ALIGN_LEFT.equalsIgnoreCase(align) //
|| Styles.ALIGN_RIGHT.equalsIgnoreCase(align))
return align;
}
return null;
}
private void receiveStyleContent(IStyle style, String styleContent) {
String[] items = styleContent.trim().split(";"); //$NON-NLS-1$
for (String item : items) {
item = item.trim();
int colonIndex = item.indexOf(':');
if (colonIndex > 0) {
String key = item.substring(0, colonIndex).trim();
String value = item.substring(colonIndex + 1).trim();
receiveStyleItem(style, key, value);
}
}
}
private void receiveStyleItem(IStyle style, String key, String value) {
if ("color".equalsIgnoreCase(key)) { //$NON-NLS-1$
String color = parseColor(value);
if (color != null) {
style.setProperty(Styles.TextColor, color);
}
} else if ("font-size".equalsIgnoreCase(key)) { //$NON-NLS-1$
int fontSize = NumberUtils.safeParseInt(value, -1);
if (fontSize > 0) {
style.setProperty(Styles.FontSize,
StyleUtils.addUnitPoint(fontSize));
}
}
}
private void popStyle(IStyle style) {
if (style == null)
return;
if (styleStack != null && styleStack.peek() == style) {
styleStack.pop();
}
}
private String parseColor(String color) {
return color;
// if (color != null) {
// return ColorUtils.toString(ColorUtils.toRGB(color));
// }
// return null;
}
}
private static final String DOCUMENT_XML = "Document.xml"; //$NON-NLS-1$
private static final double DPM = 72 / 25.4;
private static ResourceMappingManager mappings = null;
// private ZipFile sourceFile;
private IStorage tempStorage;
private IInputSource tempSource;
private ISheet targetSheet;
private Map<String, String> idMap = new HashMap<String, String>(30);
private IStyleSheet tempStyleSheet = null;
private Map<IStyled, IStyle> styleMap = new HashMap<IStyled, IStyle>(30);
private IStyle theme = null;
private Map<String, List<ITopic>> topicLinkMap = new HashMap<String, List<ITopic>>(
10);
public MindManagerImporter(String sourcePath) {
super(sourcePath);
}
public MindManagerImporter(String sourcePath, IWorkbook targetWorkbook) {
super(sourcePath, targetWorkbook);
}
public void build() throws InvocationTargetException, InterruptedException {
MindMapUIPlugin.getDefault().getUsageDataCollector()
.increase("ImportFromMindManagerCount"); //$NON-NLS-1$
getMonitor().beginTask(null, 100);
try {
getMonitor()
.subTask(ImportMessages.MindManagerImporter_ReadingContent);
tempStorage = createTemporaryStorage();
extractSourceFileToTemporaryStorage();
tempSource = tempStorage.getInputSource();
// sourceFile = new ZipFile(getSourcePath());
// ZipEntry docEntry = sourceFile.getEntry(DOCUMENT_XML);
Document doc = readContents();
getMonitor().worked(45);
checkInterrupted();
getMonitor().subTask(
ImportMessages.MindManagerImporter_ReadingElements);
loadSheet(doc.getDocumentElement());
setTopicLinks();
getMonitor().worked(45);
checkInterrupted();
getMonitor().subTask(
ImportMessages.MindManagerImporter_ArrangingStyles);
arrangeStyles();
getMonitor().worked(5);
checkInterrupted();
getMonitor().subTask(
ImportMessages.MindManagerImporter_GeneratingTheme);
generateTheme();
getMonitor().worked(5);
getMonitor().done();
} catch (Exception e) {
throw new InvocationTargetException(e);
} finally {
clearTempStorage();
}
postBuilded();
}
private Document readContents() throws Exception {
InputStream docEntryStream = tempSource.getEntryStream(DOCUMENT_XML);
if (docEntryStream == null)
throw new IOException("No content entry"); //$NON-NLS-1$
DocumentBuilder builder = getDocumentBuilder();
builder.setErrorHandler(this);
// InputStream in = sourceFile.getInputStream(docEntryStream);
// in = new MonitoredInputStream(in, getMonitor());
InputStream in = new MonitoredInputStream(docEntryStream, getMonitor());
Document doc;
try {
doc = builder.parse(in);
} finally {
builder.setErrorHandler(null);
try {
in.close();
} catch (Exception e) {
}
}
return doc;
}
/**
* @return
*/
private IStorage createTemporaryStorage() throws IOException {
String id = String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", //$NON-NLS-1$
System.currentTimeMillis());
File tempDir = FileUtils.ensureDirectory(new File(
Core.getWorkspace().getTempDir("import/mindmanager"), id)); //$NON-NLS-1$
return new DirectoryStorage(tempDir);
}
private void extractSourceFileToTemporaryStorage()
throws IOException, CoreException {
FileInputStream fin = new FileInputStream(getSourcePath());
try {
ZipInputStream zin = new ZipInputStream(new MonitoredInputStream(
new BufferedInputStream(fin), getMonitor()));
try {
FileUtils.extractZipFile(zin, tempStorage.getOutputTarget());
} finally {
zin.close();
}
} finally {
fin.close();
}
}
private void checkInterrupted() throws InterruptedException {
if (getMonitor().isCanceled())
throw new InterruptedException();
}
private void clearTempStorage() {
if (tempStorage != null) {
tempStorage.clear();
tempStorage = null;
}
// if (sourceFile != null) {
// try {
// sourceFile.close();
// } catch (IOException e) {
// }
// }
// sourceFile = null;
}
private void loadSheet(Element docEle) throws InterruptedException {
checkInterrupted();
targetSheet = getTargetWorkbook().createSheet();
Element oneTopicEle = child(docEle, "OneTopic"); //$NON-NLS-1$
if (oneTopicEle != null) {
loadRootTopic(oneTopicEle);
}
Element relsEle = child(docEle, "Relationships"); //$NON-NLS-1$
if (relsEle != null) {
loadRelationships(relsEle);
}
Element styleGroupEle = child(docEle, "StyleGroup"); //$NON-NLS-1$
if (styleGroupEle != null) {
loadStyleGroup(styleGroupEle);
}
Element markersGroupEle = child(docEle, "MarkersSetGroup"); //$NON-NLS-1$
if (markersGroupEle != null) {
loadMarkersGroup(markersGroupEle);
}
addTargetSheet(targetSheet);
}
public ISheet getTargetSheet() {
return targetSheet;
}
private void arrangeStyles() throws InterruptedException {
IStyleSheet targetStyleSheet = getTargetWorkbook().getStyleSheet();
for (Entry<IStyled, IStyle> en : styleMap.entrySet()) {
checkInterrupted();
IStyled styleOwner = en.getKey();
IStyle style = en.getValue();
IStyle importedStyle = targetStyleSheet.importStyle(style);
if (importedStyle != null) {
styleOwner.setStyleId(importedStyle.getId());
}
}
}
private void loadMarkersGroup(Element markersGroupEle)
throws InterruptedException {
checkInterrupted();
ILegend legend = getTargetSheet().getLegend();
Element markersSetsEle = child(markersGroupEle, "ap:IconMarkersSets"); //$NON-NLS-1$
if (markersSetsEle != null) {
Iterator<Element> it = children(markersSetsEle,
"ap:IconMarkersSet"); //$NON-NLS-1$
while (it.hasNext()) {
checkInterrupted();
Element markersSetEle = it.next();
loadLegendMarkers(markersSetEle, legend, "ap:IconMarkers", //$NON-NLS-1$
"ap:IconMarker", //$NON-NLS-1$
"ap:OneStockIcon", //$NON-NLS-1$
"IconType"); //$NON-NLS-1$
}
}
loadLegendMarkers(markersGroupEle, legend, "ap:IconMarkers", //$NON-NLS-1$
"ap:IconMarker", //$NON-NLS-1$
"ap:OneStockIcon", //$NON-NLS-1$
"IconType"); //$NON-NLS-1$
loadLegendMarkers(markersGroupEle, legend, "ap:TaskPercentageMarkers", //$NON-NLS-1$
"ap:TaskPercentageMarker", //$NON-NLS-1$
"ap:TaskPercentage", //$NON-NLS-1$
"TaskPercentage"); //$NON-NLS-1$
loadLegendMarkers(markersGroupEle, legend, "ap:TaskPriorityMarkers", //$NON-NLS-1$
"ap:TaskPriorityMarker", //$NON-NLS-1$
"ap:TaskPriority", //$NON-NLS-1$
"TaskPriority" //$NON-NLS-1$
);
}
private void loadLegendMarkers(Element markersSetEle, ILegend legend,
String markersEleName, String markerEleName, String iconEleName,
String iconAttrName) throws InterruptedException {
checkInterrupted();
Element markersEle = child(markersSetEle, markersEleName);
if (markersEle != null)
loadLegendMarkers(markersEle, legend, markerEleName, iconEleName,
iconAttrName);
}
private void loadLegendMarkers(Element markersEle, ILegend legend,
String markerEleName, String iconEleName, String iconAttrName)
throws InterruptedException {
checkInterrupted();
Iterator<Element> it = children(markersEle, markerEleName);
while (it.hasNext()) {
checkInterrupted();
Element markerEle = it.next();
Element iconEle = child(markerEle, iconEleName);
if (iconEle != null) {
String type = att(iconEle, iconAttrName);
if (type != null) {
String markerId = getMapping("marker", type, null); //$NON-NLS-1$
if (markerId != null) {
String name = loadName(markerEle);
legend.setMarkerDescription(markerId, name);
}
}
}
}
}
private static String loadName(Element parentEle) {
return loadName(parentEle, null);
}
private static String loadName(Element parentEle, String eleName) {
if (eleName == null)
eleName = "ap:Name"; //$NON-NLS-1$
Element ele = child(parentEle, eleName);
if (ele != null) {
return att(ele, "Name"); //$NON-NLS-1$
}
return null;
}
private void loadStyleGroup(Element styleGroupEle)
throws InterruptedException {
checkInterrupted();
loadTopicTheme(styleGroupEle);
loadRelTheme(styleGroupEle);
loadBoundaryTheme(styleGroupEle);
loadSheetStyle(styleGroupEle);
}
private void loadSheetStyle(Element styleGroupEle)
throws InterruptedException {
checkInterrupted();
ISheet sheet = getTargetSheet();
Element structureEle = child(styleGroupEle, "ap:Structure"); //$NON-NLS-1$
if (structureEle != null) {
Float lineWidth = parseFloat(
att(structureEle, "MainTopicLineWidth")); //$NON-NLS-1$
if (lineWidth != null && lineWidth.floatValue() > 3.0f) {
registerStyle(sheet, Styles.LineTapered, Styles.TAPERED);
}
}
Element bgFillEle = child(styleGroupEle, "ap:BackgroundFill"); //$NON-NLS-1$
if (bgFillEle != null) {
String fillColor = parseColor(att(bgFillEle, "FillColor")); //$NON-NLS-1$
if (fillColor != null) {
registerStyle(sheet, Styles.FillColor, fillColor);
}
}
Element bgImgEle = child(styleGroupEle, "ap:BackgroundImageData"); //$NON-NLS-1$
if (bgImgEle != null) {
String uri = loadUri(bgImgEle);
if (uri != null) {
IFileEntry entry = loadAttachment(null, uri, "*.png"); //$NON-NLS-1$
if (entry != null && entry.getSize() > 0) {
registerStyle(sheet, Styles.Background,
HyperlinkUtils.toAttachmentURL(entry.getPath()));
int transparency = NumberUtils
.safeParseInt(att(bgImgEle, "Transparency"), -1); //$NON-NLS-1$
if (transparency >= 0) {
double opacity = (100 - transparency) * 1.0 / 100;
registerStyle(sheet, Styles.Opacity,
String.format("%.2f", opacity)); //$NON-NLS-1$
}
}
}
}
}
private void loadBoundaryTheme(Element styleGroupEle)
throws InterruptedException {
checkInterrupted();
Element parentEle = child(styleGroupEle, "ap:BoundaryDefaultsGroup"); //$NON-NLS-1$
if (parentEle == null)
return;
loadThemeColor(parentEle, true, true, true, IStyle.BOUNDARY,
Styles.FAMILY_BOUNDARY);
loadThemeLineStyle(parentEle, IStyle.BOUNDARY, Styles.FAMILY_BOUNDARY);
loadThemeBoundaryShape(parentEle, Styles.FAMILY_BOUNDARY);
}
private void loadThemeBoundaryShape(Element parentEle, String styleFamily)
throws InterruptedException {
checkInterrupted();
Element ele = child(parentEle, "ap:DefaultBoundaryShape"); //$NON-NLS-1$
if (ele == null)
return;
String shape = parseBoundaryShape(att(ele, "BoundaryShape")); //$NON-NLS-1$
registerTheme(IStyle.BOUNDARY, styleFamily, Styles.ShapeClass, shape);
}
private void loadThemeLineStyle(Element parentEle, String type,
String styleFamily) throws InterruptedException {
checkInterrupted();
Element ele = child(parentEle, "ap:DefaultLineStyle"); //$NON-NLS-1$
if (ele == null)
return;
String linePattern = parseLinePattern(att(ele, "LineDashStyle")); //$NON-NLS-1$
registerTheme(type, styleFamily, Styles.LinePattern, linePattern);
String lineWidth = parseLineWidth(att(ele, "LineWidth")); //$NON-NLS-1$
registerTheme(type, styleFamily, Styles.LineWidth, lineWidth);
}
private void loadRelTheme(Element styleGroupEle)
throws InterruptedException {
checkInterrupted();
Element parentEle = child(styleGroupEle,
"ap:RelationshipDefaultsGroup"); //$NON-NLS-1$
if (parentEle == null)
return;
loadThemeColor(parentEle, false, false, true, IStyle.RELATIONSHIP,
Styles.FAMILY_RELATIONSHIP);
loadThemeLineStyle(parentEle, IStyle.RELATIONSHIP,
Styles.FAMILY_RELATIONSHIP);
loadThemeArrowStyle(parentEle, Styles.FAMILY_RELATIONSHIP);
loadThemeRelShape(parentEle, Styles.FAMILY_RELATIONSHIP);
}
private void loadThemeRelShape(Element parentEle, String styleFamily)
throws InterruptedException {
checkInterrupted();
Element relShapeEle = child(parentEle,
"ap:DefaultRelationshipLineShape"); //$NON-NLS-1$
if (relShapeEle == null)
return;
String shape = parseRelationshipShape(att(relShapeEle, "LineShape")); //$NON-NLS-1$
registerTheme(IStyle.RELATIONSHIP, styleFamily, Styles.ShapeClass,
shape);
}
private void loadThemeArrowStyle(Element parentEle, String styleFamily)
throws InterruptedException {
checkInterrupted();
Iterator<Element> it = children(parentEle, "ap:DefaultConnectionStyle"); //$NON-NLS-1$
while (it.hasNext()) {
checkInterrupted();
Element ele = it.next();
int index = NumberUtils.safeParseInt(att(ele, "Index"), -1); //$NON-NLS-1$
if (index == 0 || index == 1) {
String shape = parseConnShape(att(ele, "ConnectionShape")); //$NON-NLS-1$
if (shape != null) {
String styleKey = index == 0 ? Styles.ArrowBeginClass
: Styles.ArrowEndClass;
registerTheme(IStyle.RELATIONSHIP, styleFamily, styleKey,
shape);
}
}
}
}
private void loadThemeColor(Element parentEle, boolean fill,
boolean fillAlpha, boolean line, String type, String styleFamily)
throws InterruptedException {
checkInterrupted();
Element colorEle = child(parentEle, "ap:DefaultColor"); //$NON-NLS-1$
if (colorEle == null)
return;
if (fill || fillAlpha) {
String fillColor = att(colorEle, "FillColor"); //$NON-NLS-1$
if (fill) {
registerTheme(type, styleFamily, Styles.FillColor,
parseColor(fillColor));
}
if (fillAlpha) {
registerTheme(type, styleFamily, Styles.Opacity,
parseAlpha(fillColor));
}
}
if (line) {
String lineColor = att(colorEle, "LineColor"); //$NON-NLS-1$
registerTheme(type, styleFamily, Styles.LineColor,
parseColor(lineColor));
}
}
private void loadTopicTheme(Element styleGroupEle)
throws InterruptedException {
checkInterrupted();
Element rootEle = child(styleGroupEle, "ap:RootTopicDefaultsGroup"); //$NON-NLS-1$
if (rootEle != null) {
loadTopicTheme(rootEle, null, null, Styles.FAMILY_CENTRAL_TOPIC);
}
int deepestLevel = 0;
Element realSubEle = null;
Iterator<Element> it = children(rootEle,
"ap:RootSubTopicDefaultsGroup"); //$NON-NLS-1$
while (it.hasNext()) {
Element subEle = it.next();
int level = NumberUtils.safeParseInt(att(subEle, "Level"), -1); //$NON-NLS-1$
if (level == 0) {
loadTopicTheme(subEle, null, null, Styles.FAMILY_MAIN_TOPIC);
} else if (level > 0) {
if (level > deepestLevel) {
deepestLevel = level;
realSubEle = subEle;
}
}
}
if (realSubEle != null) {
loadTopicTheme(realSubEle, null, null, Styles.FAMILY_SUB_TOPIC);
}
Element floatingEle = child(styleGroupEle,
"ap:LabelTopicDefaultsGroup"); //$NON-NLS-1$
if (floatingEle != null) {
loadTopicTheme(floatingEle, "ap:DefaultLabelFloatingTopicShape", //$NON-NLS-1$
"LabelFloatingTopicShape", Styles.FAMILY_FLOATING_TOPIC); //$NON-NLS-1$
}
}
private void loadTopicTheme(Element parentEle, String shapeEleName,
String shapeAttrName, String styleFamily)
throws InterruptedException {
checkInterrupted();
loadThemeColor(parentEle, true, false, true, IStyle.TOPIC, styleFamily);
loadThemeTextStyle(parentEle, styleFamily);
loadThemeTopicShape(parentEle, shapeEleName, shapeAttrName,
styleFamily);
loadThemeBranchStyle(parentEle, styleFamily);
}
private void loadThemeBranchStyle(Element parentEle, String styleFamily)
throws InterruptedException {
checkInterrupted();
Element ele = child(parentEle, "ap:DefaultSubTopicsShape"); //$NON-NLS-1$
if (ele == null)
return;
String branchConn = parseBranchConn(
att(ele, "SubTopicsConnectionStyle")); //$NON-NLS-1$
registerTheme(IStyle.TOPIC, styleFamily, Styles.LineClass, branchConn);
}
private static String parseBranchConn(String connStyle) {
return getMapping("branchConnection", connStyle, null); //$NON-NLS-1$
}
private void loadThemeTopicShape(Element parentEle, String shapeEleName,
String shapeAttrName, String styleFamily)
throws InterruptedException {
checkInterrupted();
if (shapeEleName == null)
shapeEleName = "ap:DefaultSubTopicShape"; //$NON-NLS-1$
Element shapeEle = child(parentEle, shapeEleName);
if (shapeEle == null)
return;
if (shapeAttrName == null)
shapeAttrName = "SubTopicShape"; //$NON-NLS-1$
String shape = parseTopicShape(att(shapeEle, shapeAttrName));
registerTheme(IStyle.TOPIC, styleFamily, Styles.ShapeClass, shape);
}
private void loadThemeTextStyle(Element parentEle, String themeKey)
throws InterruptedException {
checkInterrupted();
Element textEle = child(parentEle, "ap:Text"); //$NON-NLS-1$
if (textEle == null)
return;
loadThemeFont(textEle, IStyle.TOPIC, themeKey);
}
private void loadThemeFont(Element parentEle, String type,
String styleFamily) throws InterruptedException {
checkInterrupted();
Element fontEle = child(parentEle, "ap:Font"); //$NON-NLS-1$
if (fontEle == null)
return;
String color = parseColor(att(fontEle, "Color")); //$NON-NLS-1$
registerTheme(type, styleFamily, Styles.TextColor, color);
String fontName = att(fontEle, "Name"); //$NON-NLS-1$
String availableFontName = FontUtils.getAAvailableFontNameFor(fontName);
fontName = availableFontName != null ? availableFontName : fontName;
registerTheme(type, styleFamily, Styles.FontFamily, fontName);
String size = att(fontEle, "Size"); //$NON-NLS-1$
registerTheme(type, styleFamily, Styles.FontSize, size);
String bold = att(fontEle, "Bold"); //$NON-NLS-1$
registerTheme(type, styleFamily, Styles.FontWeight,
Boolean.parseBoolean(bold) ? Styles.FONT_WEIGHT_BOLD : null);
String italic = att(fontEle, "Italic"); //$NON-NLS-1$
registerTheme(type, styleFamily, Styles.FontStyle,
Boolean.parseBoolean(italic) ? Styles.FONT_STYLE_ITALIC : null);
boolean underline = Boolean.parseBoolean(att(fontEle, "Underline")); //$NON-NLS-1$
boolean strikeout = Boolean.parseBoolean(att(fontEle, "Strikethrough")); //$NON-NLS-1$
registerTheme(type, styleFamily, Styles.TextDecoration,
StyleUtils.toTextDecoration(underline, strikeout));
}
private void registerTheme(String type, String styleFamily, String styleKey,
String styleValue) throws InterruptedException {
checkInterrupted();
if (styleFamily == null || styleKey == null || styleValue == null)
return;
if (theme == null) {
theme = getTempStyleSheet().createStyle(IStyle.THEME);
getTempStyleSheet().addStyle(theme, IStyleSheet.MASTER_STYLES);
}
IStyle defaultStyle = theme.getDefaultStyle(styleFamily);
if (defaultStyle == null) {
defaultStyle = getTempStyleSheet().createStyle(type);
getTempStyleSheet().addStyle(defaultStyle,
IStyleSheet.AUTOMATIC_STYLES);
}
defaultStyle.setProperty(styleKey, styleValue);
}
private void generateTheme() throws InterruptedException {
checkInterrupted();
if (theme != null) {
IStyle importedTheme = getTargetWorkbook().getStyleSheet()
.importStyle(theme);
if (importedTheme != null) {
getTargetSheet().setThemeId(importedTheme.getId());
}
}
}
private void loadRelationships(Element relsEle)
throws InterruptedException {
checkInterrupted();
Iterator<Element> it = children(relsEle, "ap:Relationship"); //$NON-NLS-1$
while (it.hasNext()) {
Element relEle = it.next();
loadRelationship(relEle);
}
}
private void loadRelationship(Element relEle) throws InterruptedException {
checkInterrupted();
IRelationship rel = getTargetWorkbook().createRelationship();
getTargetSheet().addRelationship(rel);
loadOId(relEle, rel);
boolean autoRouting = loadAutoRouting(relEle);
loadConnections(relEle, rel, autoRouting);
loadLineStyle(relEle, rel);
loadRelLineShape(relEle, rel);
}
private void loadRelLineShape(Element relEle, IRelationship rel)
throws InterruptedException {
checkInterrupted();
Element relShapeEle = child(relEle, "ap:RelationshipLineShape"); //$NON-NLS-1$
if (relShapeEle == null)
return;
String shape = parseRelationshipShape(att(relShapeEle, "LineShape")); //$NON-NLS-1$
registerStyle(rel, Styles.ShapeClass, shape);
}
private static String parseRelationshipShape(String shape) {
return getMapping("relationshipShape", shape, null); //$NON-NLS-1$
}
private void loadConnections(Element relEle, IRelationship rel,
boolean autoRouting) throws InterruptedException {
checkInterrupted();
Iterator<Element> it = children(relEle, "ap:ConnectionGroup"); //$NON-NLS-1$
while (it.hasNext()) {
Element connGroupEle = it.next();
loadRelConnection(connGroupEle, rel, autoRouting);
}
}
private void loadRelConnection(Element connGroupEle, IRelationship rel,
boolean autoRouting) throws InterruptedException {
checkInterrupted();
int index = NumberUtils.safeParseInt(att(connGroupEle, "Index"), -1); //$NON-NLS-1$
if (index != 0 && index != 1)
return;
Element connEle = child(connGroupEle, "ap:Connection"); //$NON-NLS-1$
if (connEle == null)
return;
Element refEle = child(connEle, "ap:ObjectReference"); //$NON-NLS-1$
if (refEle == null)
return;
String oIdRef = att(refEle, "OIdRef"); //$NON-NLS-1$
String endId = idMap.get(oIdRef);
if (endId == null)
return;
if (index == 0) {
rel.setEnd1Id(endId);
} else {
rel.setEnd2Id(endId);
}
if (!autoRouting || connEle != null) {
String cx = att(connEle, "CX"); //$NON-NLS-1$
String cy = att(connEle, "CY"); //$NON-NLS-1$
if (cx != null && cy != null) {
Float x = parseFloat(cx);
Float y = parseFloat(cy);
if (x != null && y != null) {
rel.getControlPoint(index).setPosition(
mm2Dots(x.floatValue()), mm2Dots(y.floatValue()));
}
}
}
Element connStyleEle = child(connGroupEle, "ap:ConnectionStyle"); //$NON-NLS-1$
if (connStyleEle != null) {
String arrowShape = parseConnShape(
att(connStyleEle, "ConnectionShape")); //$NON-NLS-1$
String styleKey = index == 0 ? Styles.ArrowBeginClass
: Styles.ArrowEndClass;
registerStyle(rel, styleKey, arrowShape);
}
}
private static String parseConnShape(String shape) {
return getMapping("arrowShape", shape, null); //$NON-NLS-1$
}
private boolean loadAutoRouting(Element relEle)
throws InterruptedException {
checkInterrupted();
Element autoRouteEle = child(relEle, "ap:AutoRoute"); //$NON-NLS-1$
if (autoRouteEle != null) {
return Boolean.parseBoolean(att(autoRouteEle, "AutoRouting")); //$NON-NLS-1$
}
return false;
}
private void loadRootTopic(Element oneTopicEle)
throws InterruptedException {
checkInterrupted();
Element topicEle = child(oneTopicEle, "Topic"); //$NON-NLS-1$
if (topicEle != null) {
loadTopicContent(topicEle, getTargetSheet().getRootTopic());
}
}
private void loadTopicContent(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
loadOId(topicEle, topic);
loadTitle(topicEle, topic);
loadColor(topicEle, topic, false);
loadPosition(topicEle, topic);
loadTopicViewGroup(topicEle, topic);
loadImage(topicEle, topic);
loadTopicShape(topicEle, topic);
loadHyperlink(topicEle, topic);
loadMarkers(topicEle, topic);
loadLabels(topicEle, topic);
loadNotes(topicEle, topic);
loadTask(topicEle, topic);
loadNumbering(topicEle, topic);
loadSubTopics(topicEle, topic);
loadDetachedSubTopics(topicEle, topic);
loadBoundary(topicEle, topic);
loadStructure(topicEle, topic);
loadAttachments(topicEle, topic);
}
private void loadAttachments(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element attGroupEle = child(topicEle, "ap:AttachmentGroup"); //$NON-NLS-1$
if (attGroupEle == null)
return;
Iterator<Element> it = children(attGroupEle, "ap:AttachmentData"); //$NON-NLS-1$
while (it.hasNext()) {
Element attDataEle = it.next();
String uri = loadUri(attDataEle);
if (uri != null) {
String oId = att(attDataEle, "AttachmentId"); //$NON-NLS-1$
String name = att(attDataEle, "FileName"); //$NON-NLS-1$
IFileEntry entry = loadAttachment(oId, uri, name);
if (entry != null) {
if (name == null)
name = new File(entry.getPath()).getName();
ITopic attTopic = getTargetWorkbook().createTopic();
attTopic.setTitleText(name);
attTopic.setHyperlink(
HyperlinkUtils.toAttachmentURL(entry.getPath()));
topic.add(attTopic, ITopic.ATTACHED);
}
}
}
}
private void loadStructure(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element subTopicsShapeEle = child(topicEle, "ap:SubTopicsShape"); //$NON-NLS-1$
if (subTopicsShapeEle == null)
return;
String line = parseBranchConnection(
att(subTopicsShapeEle, "SubTopicsConnectionStyle")); //$NON-NLS-1$
registerStyle(topic, Styles.LineClass, line);
String structureClass = parseStructureType(subTopicsShapeEle);
topic.setStructureClass(structureClass);
}
private static String parseStructureType(Element ele) {
String align = att(ele, "SubTopicsAlignment"); //$NON-NLS-1$
String growth = att(ele, "SubTopicsGrowth"); //$NON-NLS-1$
String growthDir = att(ele, "SubTopicsGrowthDirection"); //$NON-NLS-1$
if (umCenter.equals(align) && umHorizontal.equals(growth)) {
if (umLeftAndRight.equals(growthDir))
return "org.xmind.ui.map.clockwise"; //$NON-NLS-1$
if (umRight.equals(growthDir))
return "org.xmind.ui.logic.right"; //$NON-NLS-1$
if (umLeft.equals(growthDir))
return "org.xmind.ui.logic.left"; //$NON-NLS-1$
} else if (umVertical.equals(growth)
&& umMiddle.equals(att(ele, "SubTopicsVerticalAlignment"))) { //$NON-NLS-1$
String vgd = att(ele, "SubTopicsVerticalGrowthDirection"); //$NON-NLS-1$
if (umDown.equals(vgd) || umUpAndDown.equals(vgd))
return "org.xmind.ui.org-chart.down"; //$NON-NLS-1$
if (umUp.equals(vgd))
return "org.xmind.ui.org-chart.up"; //$NON-NLS-1$
} else if (umBottom.equals(align) && umHorizontal.equals(growth)) {
if (umRight.equals(growthDir) || umLeftAndRight.equals(growthDir))
return "org.xmind.ui.tree.right"; //$NON-NLS-1$
if (umLeft.equals(growthDir))
return "org.xmind.ui.tree.left"; //$NON-NLS-1$
}
return null;
}
private static String parseBranchConnection(String lineShape) {
return getMapping("branchConnection", lineShape, null); //$NON-NLS-1$
}
private void loadBoundary(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
if (topic.isRoot())
return;
Element oneBoundaryEle = child(topicEle, "ap:OneBoundary"); //$NON-NLS-1$
if (oneBoundaryEle == null)
return;
Element boundaryEle = child(oneBoundaryEle, "ap:Boundary"); //$NON-NLS-1$
if (boundaryEle == null)
return;
checkInterrupted();
String boundaryShape = getBoundaryShape(boundaryEle);
String shape = parseSummaryShape(boundaryShape);
ITopicRange range;
ITopic parent;
if (shape != null && topic.isAttached()) {
ISummary summary = getTargetWorkbook().createSummary();
parent = topic.getParent();
int index = topic.getIndex();
summary.setStartIndex(index);
summary.setEndIndex(index);
parent.addSummary(summary);
range = summary;
} else {
shape = parseBoundaryShape(boundaryShape);
IBoundary boundary = getTargetWorkbook().createBoundary();
if (topic.isAttached()) {
parent = topic.getParent();
int index = topic.getIndex();
boundary.setStartIndex(index);
boundary.setEndIndex(index);
parent.addBoundary(boundary);
} else {
parent = null;
boundary.setMasterBoundary(true);
topic.addBoundary(boundary);
}
range = boundary;
}
loadOId(boundaryEle, (IIdentifiable) range);
loadColor(boundaryEle, (IStyled) range, true);
loadLineStyle(boundaryEle, (IStyled) range);
registerStyle((IStyled) range, Styles.ShapeClass, shape);
if (range instanceof ISummary && parent != null) {
ISummary summary = (ISummary) range;
ITopic summaryTopic = getTargetWorkbook().createTopic();
parent.add(summaryTopic, ITopic.SUMMARY);
summary.setTopicId(summaryTopic.getId());
Element summaryTopicEle = getSummaryTopicEle(boundaryEle);
if (summaryTopicEle != null) {
loadTopicContent(summaryTopicEle, summaryTopic);
} else {
summaryTopic.setTitleText(" "); //$NON-NLS-1$
registerStyle(summaryTopic, Styles.ShapeClass,
"org.xmind.topicShape.noBorder"); //$NON-NLS-1$
}
} else if (range instanceof IBoundary) {
IBoundary boundary = (IBoundary) range;
Element boundaryTopicEle = getSummaryTopicEle(boundaryEle);
if (boundaryTopicEle != null) {
loadTitle(boundaryTopicEle, boundary);
}
}
}
private Element getSummaryTopicEle(Element boundaryEle) {
Element oneSummaryTopicEle = child(boundaryEle, "ap:OneSummaryTopic"); //$NON-NLS-1$
if (oneSummaryTopicEle != null) {
return child(oneSummaryTopicEle, "ap:Topic"); //$NON-NLS-1$
}
return null;
}
private String getBoundaryShape(Element boundaryEle) {
Element shapeEle = child(boundaryEle, "ap:BoundaryShape"); //$NON-NLS-1$
if (shapeEle != null) {
return att(shapeEle, "BoundaryShape"); //$NON-NLS-1$
}
return null;
}
private static String parseBoundaryShape(String shape) {
return getMapping("boundaryShape", shape, null); //$NON-NLS-1$
}
private static String parseSummaryShape(String shape) {
return getMapping("summaryShape", shape, null); //$NON-NLS-1$
}
private void loadLineStyle(Element parentEle, IStyled host)
throws InterruptedException {
checkInterrupted();
Element lineColor = child(parentEle, "ap:Color"); //$NON-NLS-1$
if (lineColor != null) {
String color = att(lineColor, "LineColor");//$NON-NLS-1$
if (color != null) {
registerStyle(host, Styles.LineColor, "#" + color.substring(2)); //$NON-NLS-1$
}
}
Element lineStyleEle = child(parentEle, "ap:LineStyle"); //$NON-NLS-1$
if (lineStyleEle == null)
return;
String linePattern = parseLinePattern(
att(lineStyleEle, "LineDashStyle")); //$NON-NLS-1$
registerStyle(host, Styles.LinePattern, linePattern);
String width = parseLineWidth(att(lineStyleEle, "LineWidth")); //$NON-NLS-1$
registerStyle(host, Styles.LineWidth, width);
}
private static String parseLineWidth(String width) {
Float w = parseFloat(width);
if (w != null) {
if (w <= 1.0)
return "1"; //$NON-NLS-1$
if (w <= 2.25)
return "2"; //$NON-NLS-1$
if (w <= 3.0)
return "3"; //$NON-NLS-1$
if (w <= 4.5)
return "4"; //$NON-NLS-1$
return "5"; //$NON-NLS-1$
}
return null;
}
private static String parseLinePattern(String dashStyle) {
return getMapping("lineStyle", dashStyle, null); //$NON-NLS-1$
}
private void loadDetachedSubTopics(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element subTopicsEle = child(topicEle, "ap:FloatingTopics"); //$NON-NLS-1$
if (subTopicsEle != null) {
Iterator<Element> it = children(subTopicsEle, "ap:Topic"); //$NON-NLS-1$
while (it.hasNext()) {
Element subTopicEle = it.next();
ITopic subTopic = getTargetWorkbook().createTopic();
Element item = child(subTopicEle, "ap:Offset"); //$NON-NLS-1$
if (item != null && item.hasAttribute("OffsetPriority")) //$NON-NLS-1$
topic.add(subTopic, ITopic.DETACHED);
else
topic.add(subTopic, ITopic.CALLOUT);
loadTopicContent(subTopicEle, subTopic);
}
}
}
private void loadSubTopics(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element subTopicsEle = child(topicEle, "ap:SubTopics"); //$NON-NLS-1$
if (subTopicsEle != null) {
Iterator<Element> it = children(subTopicsEle, "ap:Topic"); //$NON-NLS-1$
while (it.hasNext()) {
Element subTopicEle = it.next();
ITopic subTopic = getTargetWorkbook().createTopic();
topic.add(subTopic, ITopic.ATTACHED);
loadTopicContent(subTopicEle, subTopic);
}
}
}
private final static String NUMBER_FORMAT_NONE = "org.xmind.numbering.none"; //$NON-NLS-1$
private final static String NUMBER_FORMAT_ARABIC = "org.xmind.numbering.arabic"; //$NON-NLS-1$
private final static String NUMBER_FORMAT_ROMAN = "org.xmind.numbering.roman"; //$NON-NLS-1$
private final static String NUMBER_FORMAT_LOWERCASE = "org.xmind.numbering.lowercase"; //$NON-NLS-1$
private final static String NUMBER_FORMAT_UPPERCASE = "org.xmind.numbering.uppercase"; //$NON-NLS-1$
private Map<ITopic, Element> numberingEles;
private void loadNumbering(Element topicEle, ITopic topic) {
Element numberingEle = findNumberingEle(topicEle, topic);
if (numberingEle == null)
return;
int maxDepth = getMaxDepth(numberingEle);
int topicDepth = getTopicDepth(topic);
String numberFormat = getNumberFormat(numberingEle, topicDepth);
if (numberFormat == null)
return;
String prefix = getNumberingPrefix(numberingEle, topicDepth);
INumbering numbering = topic.getNumbering();
if ((maxDepth - topicDepth) >= 0)
numbering.setFormat(numberFormat);
else
numbering.setFormat(NUMBER_FORMAT_NONE);
if (topicDepth == 1)
numbering.setPrependsParentNumbers(false);
numbering.setPrefix(prefix);
}
private String getNumberFormat(Element numberingEle, int depth) {
String numbering = att(numberingEle, "cst1:Numbering"); //$NON-NLS-1$
if (numbering == null)
return null;
int formatIndex = (depth - 1) * 2;
if (formatIndex < 0 || (formatIndex + 1) > numbering.length())
return NUMBER_FORMAT_NONE;
String numberFormat = numbering.substring(formatIndex, formatIndex + 1);
if ("1".equals(numberFormat)) //$NON-NLS-1$
return NUMBER_FORMAT_ARABIC;
if ("I".equals(numberFormat) || "i".equals(numberFormat)) //$NON-NLS-1$ //$NON-NLS-2$
return NUMBER_FORMAT_ROMAN;
if ("A".equals(numberFormat)) //$NON-NLS-1$
return NUMBER_FORMAT_UPPERCASE;
if ("a".equals(numberFormat)) //$NON-NLS-1$
return NUMBER_FORMAT_LOWERCASE;
return NUMBER_FORMAT_NONE;
}
private String getNumberingPrefix(Element numberingEle, int topicDepth) {
String preFix = att(numberingEle, "cst0:Level" + topicDepth + "Text"); //$NON-NLS-1$ //$NON-NLS-2$
return preFix == null ? "" : preFix; //$NON-NLS-1$
}
private int getMaxDepth(Element numberingEle) {
String depth = att(numberingEle, "cst1:Depth"); //$NON-NLS-1$
return Integer.parseInt(depth == null ? "1" //$NON-NLS-1$
: depth);
}
private int getTopicDepth(ITopic topic) {
if (numberingEles == null)
return 0;
int topicDepth = 1;
while (topic != null) {
Element numberingEle = numberingEles.get(topic);
if (numberingEle != null)
return topicDepth;
topic = topic.getParent();
topicDepth++;
}
return 0;
}
private Element findNumberingEle(Element topicEle, ITopic topic) {
if (topicEle == null)
return null;
Element numberingEle = child(topicEle, "cor:Custom"); //$NON-NLS-1$
if (numberingEle != null) {
if (numberingEles == null)
numberingEles = new HashMap<ITopic, Element>();
numberingEles.put(topic, numberingEle);
return numberingEle;
}
return findParentNumbering(topic);
}
private Element findParentNumbering(ITopic topic) {
if (numberingEles == null)
return null;
int topicDepth = 1;
while (topic.getParent() != null) {
topic = topic.getParent();
Element numberingEle = numberingEles.get(topic);
if (numberingEle != null
&& (getMaxDepth(numberingEle) >= topicDepth))
return numberingEle;
topicDepth++;
}
return null;
}
private void loadNotes(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element notesGroupEle = child(topicEle, "ap:NotesGroup"); //$NON-NLS-1$
if (notesGroupEle == null)
return;
// Iterator<Element> it = children(notesGroupEle, "ap:NotesData"); //$NON-NLS-1$
// while (it.hasNext()) {
// Element notesDataEle = it.next();
// String imgUri = att(notesDataEle, "ImageUri"); //$NON-NLS-1$
// String binaryUri = loadUri(notesDataEle);
// uriMap.put(imgUri, binaryUri);
// loadImageAtt(imgUri, binaryUri);
// }
Element notesXhtmlDataEle = child(notesGroupEle, "ap:NotesXhtmlData"); //$NON-NLS-1$
if (notesXhtmlDataEle != null) {
String bookmarks = getBookmarks(topicEle, topic);
String plain = att(notesXhtmlDataEle, "PreviewPlainText"); //$NON-NLS-1$
if (plain != null) {
IPlainNotesContent content = (IPlainNotesContent) getTargetWorkbook()
.createNotesContent(INotes.PLAIN);
content.setTextContent(bookmarks + plain);
topic.getNotes().setContent(INotes.PLAIN, content);
}
Element htmlEle = child(notesXhtmlDataEle, "xhtml:html"); //$NON-NLS-1$
if (htmlEle != null) {
IHtmlNotesContent content = (IHtmlNotesContent) getTargetWorkbook()
.createNotesContent(INotes.HTML);
loadHtmlNotes(notesGroupEle, htmlEle, content, bookmarks);
topic.getNotes().setContent(INotes.HTML, content);
}
}
}
private void loadHtmlNotes(Element notesGroupEle, Element htmlEle,
IHtmlNotesContent content, String bookmarks)
throws InterruptedException {
NotesImporter notesImporter = new NotesImporter(content);
notesImporter.addText(bookmarks);
notesImporter.loadFrom(notesGroupEle, htmlEle);
}
private void loadImageAtt(String imgUri, String uri)
throws InterruptedException {
checkInterrupted();
loadAttachment(null, uri, "*.png"); //$NON-NLS-1$
}
private String getBookmarks(Element topicEle, ITopic topic) {
Element bookmarkEle = child(topicEle, "ap:Bookmark"); //$NON-NLS-1$
if (bookmarkEle == null)
return ""; //$NON-NLS-1$
String bookmarkName = att(bookmarkEle, "Name"); //$NON-NLS-1$
if (bookmarkName != null) {
return bookmarkName + '\r';
}
return ""; //$NON-NLS-1$
}
private void loadLabels(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element labelsEle = child(topicEle, "ap:TextLabels"); //$NON-NLS-1$
if (labelsEle == null)
return;
Iterator<Element> it = children(labelsEle, "ap:TextLabel"); //$NON-NLS-1$
while (it.hasNext()) {
checkInterrupted();
String label = att(it.next(), "TextLabelName"); //$NON-NLS-1$
if (label != null) {
topic.addLabel(label);
}
}
}
private void loadMarkers(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element taskEle = child(topicEle, "ap:Task"); //$NON-NLS-1$
if (taskEle != null) {
addMarker(topic, att(taskEle, "TaskPriority")); //$NON-NLS-1$
addMarker(topic, att(taskEle, "TaskPercentage")); //$NON-NLS-1$
}
Element iconsGroupEle = child(topicEle, "ap:IconsGroup"); //$NON-NLS-1$
if (iconsGroupEle != null) {
Element iconsEle = child(iconsGroupEle, "ap:Icons"); //$NON-NLS-1$
if (iconsEle != null) {
Iterator<Element> it = children(iconsEle, "ap:Icon"); //$NON-NLS-1$
while (it.hasNext()) {
addMarker(topic, att(it.next(), "IconType")); //$NON-NLS-1$
}
}
}
}
private void addMarker(ITopic topic, String mmIconId)
throws InterruptedException {
checkInterrupted();
if (mmIconId != null) {
String markerId = parseMarkerId(mmIconId);
if (markerId != null) {
topic.addMarker(markerId);
}
}
}
@SuppressWarnings("nls")
private void addCheckPoint(ITopicExtensionElement content,
String hasCheckPoint) {
if (hasCheckPoint != null && hasCheckPoint.equals("true")) {
content.setAttribute("check-point", "true");
}
}
private static String parseMarkerId(String mmIconId) {
return getMapping("marker", mmIconId, "other-question"); //$NON-NLS-1$ //$NON-NLS-2$
}
private void loadHyperlink(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element hyperlinkEle = child(topicEle, "ap:Hyperlink"); //$NON-NLS-1$
if (hyperlinkEle == null)
return;
String url = att(hyperlinkEle, "Url"); //$NON-NLS-1$
if (url != null) {
if (url.startsWith("#xpointer(")) { //$NON-NLS-1$
Matcher m = getOIdPattern().matcher(url);
if (m.find()) {
String OId = m.group(1);
recordTopicLink(OId, topic);
}
return;
} else if (!url.startsWith("http://") //$NON-NLS-1$
&& !url.startsWith("https://")) { //$NON-NLS-1$
String path;
if (url.startsWith("\"") && url.endsWith("\"")) { //$NON-NLS-1$ //$NON-NLS-2$
path = url.substring(1, url.length() - 1);
} else {
path = url;
}
String absolute = att(hyperlinkEle, "Absolute"); //$NON-NLS-1$
url = FilePathParser.toURI(path,
absolute != null && !Boolean.parseBoolean(absolute));
}
topic.setHyperlink(url);
}
}
private static Pattern getOIdPattern() {
if (OID_PATTERN == null) {
OID_PATTERN = Pattern.compile("@OId='([^']*)'"); //$NON-NLS-1$
}
return OID_PATTERN;
}
private void loadTopicShape(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element shapeEle = child(topicEle, "ap:SubTopicShape"); //$NON-NLS-1$
if (shapeEle == null)
return;
String shape = parseTopicShape(att(shapeEle, "SubTopicShape")); //$NON-NLS-1$
registerStyle(topic, Styles.ShapeClass, shape);
}
private static String parseTopicShape(String shape) {
return getMapping("topicShape", shape, null); //$NON-NLS-1$
}
private void loadImage(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element oneImageEle = child(topicEle, "ap:OneImage"); //$NON-NLS-1$
if (oneImageEle == null)
return;
Element imageEle = child(oneImageEle, "ap:Image"); //$NON-NLS-1$
if (imageEle == null)
return;
String oId = att(imageEle, "OId"); //$NON-NLS-1$
Element imageDataEle = child(imageEle, "ap:ImageData"); //$NON-NLS-1$
if (imageDataEle == null)
return;
String uri = loadUri(imageDataEle);
IFileEntry imgEntry = loadAttachment(oId, uri, "*.png"); //$NON-NLS-1$
if (imgEntry == null)
return;
IImage image = topic.getImage();
image.setSource(HyperlinkUtils.toAttachmentURL(imgEntry.getPath()));
Element sizeEle = child(imageEle, "ap:ImageSize"); //$NON-NLS-1$
if (sizeEle != null) {
String width = att(sizeEle, "Width"); //$NON-NLS-1$
String height = att(sizeEle, "Height"); //$NON-NLS-1$
Float w = parseFloat(width);
Float h = parseFloat(height);
image.setSize(
w == null ? IImage.UNSPECIFIED : mm2Dots(w.floatValue()),
h == null ? IImage.UNSPECIFIED : mm2Dots(h.floatValue()));
}
String position = null;
Element topicLayoutEle = child(topicEle, "ap:TopicLayout"); //$NON-NLS-1$
if (topicLayoutEle != null) {
position = att(topicLayoutEle, "TopicTextAndImagePosition"); //$NON-NLS-1$
}
image.setAlignment(parseImageAlign(position));
}
private IFileEntry loadAttachment(String oId, String uri,
String proposalName) throws InterruptedException {
checkInterrupted();
if (uri == null)
return null;
if (idMap.containsKey(uri)) {
String path = idMap.get(uri);
if (path != null) {
return getTargetWorkbook().getManifest().getFileEntry(path);
}
return null;
}
String path = null;
if (uri.startsWith(MMARCH)) {
String mmEntryPath = uri.substring(MMARCH.length());
// ZipEntry mmEntry = sourceFile.getEntry(mmEntryPath);
// if (mmEntry != null) {
InputStream mmEntryStream = tempSource.getEntryStream(mmEntryPath);
if (mmEntryStream == null) {
// log(new FileNotFoundException(),
// "No such entry: " + mmEntryPath); //$NON-NLS-1$
return null;
}
// try {
// InputStream in = sourceFile.getInputStream(mmEntry);
// in = new MonitoredInputStream(in, getMonitor());
if (proposalName != null) {
if (proposalName.startsWith("*.")) { //$NON-NLS-1$
String ext = proposalName.substring(1);
String oldName = new File(mmEntryPath).getName();
proposalName = FileUtils.getNoExtensionFileName(oldName)
+ ext;
}
}
InputStream in = new MonitoredInputStream(mmEntryStream,
getMonitor());
try {
IFileEntry entry = getTargetWorkbook().getManifest()
.createAttachmentFromStream(in, proposalName);
path = entry.getPath();
} catch (IOException e) {
log(e, "Failed to create attachment from: " //$NON-NLS-1$
+ mmEntryPath);
} finally {
try {
in.close();
} catch (IOException e) {
}
}
}
idMap.put(uri, path);
return getTargetWorkbook().getManifest().getFileEntry(path);
}
private static String parseImageAlign(String position) {
return getMapping("imageAlignment", position, IImage.RIGHT); //$NON-NLS-1$
}
private static String loadUri(Element ele) {
if (ele != null) {
Element uriEle = child(ele, "cor:Uri"); //$NON-NLS-1$
if (uriEle != null) {
return uriEle.getTextContent();
}
}
return null;
}
private void loadTopicViewGroup(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element viewGroupEle = child(topicEle, "ap:TopicViewGroup"); //$NON-NLS-1$
if (viewGroupEle != null) {
Element collapsedEle = child(viewGroupEle, "ap:Collapsed"); //$NON-NLS-1$
if (collapsedEle != null) {
String c = att(collapsedEle, "Collapsed"); //$NON-NLS-1$
if (Boolean.parseBoolean(c)) {
topic.setFolded(true);
}
}
}
}
private void loadPosition(Element topicEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element offsetEle = child(topicEle, "ap:Offset"); //$NON-NLS-1$
if (offsetEle != null) {
String cx = att(offsetEle, "CX"); //$NON-NLS-1$
String cy = att(offsetEle, "CY"); //$NON-NLS-1$
Float x = parseFloat(cx);
Float y = parseFloat(cy);
if (x != null && y != null && x.floatValue() != 0
&& y.floatValue() != 0) {
topic.setPosition(mm2Dots(x), mm2Dots(y));
}
}
}
private static Float parseFloat(String value) {
if (value != null) {
try {
return Float.valueOf(value);
} catch (Throwable e) {
}
}
return null;
}
private void loadTask(Element ownerEle, ITopic topic)
throws InterruptedException {
checkInterrupted();
Element taskEle = child(ownerEle, "ap:Task"); //$NON-NLS-1$
if (taskEle == null)
return;
ITopicExtensionElement taskContent = null;
String startDate = att(taskEle, "StartDate"); //$NON-NLS-1$
if (startDate != null) {
Matcher m = DATE_PATTERN.matcher(startDate);
if (m.find()) {
taskContent = ensureTaskContent(topic, taskContent);
taskContent.deleteChildren("start-date"); //$NON-NLS-1$
ITopicExtensionElement ele = taskContent
.createChild("start-date"); //$NON-NLS-1$
ele.setTextContent(m.group(1) + " " + m.group(5)); //$NON-NLS-1$
}
}
String endDate = att(taskEle, "DeadlineDate"); //$NON-NLS-1$;
if (endDate != null) {
Matcher m = DATE_PATTERN.matcher(endDate);
if (m.find()) {
taskContent = ensureTaskContent(topic, taskContent);
taskContent.deleteChildren("end-date");//$NON-NLS-1$
ITopicExtensionElement ele = taskContent
.createChild("end-date"); //$NON-NLS-1$
ele.setTextContent(m.group(1) + " " + m.group(5)); //$NON-NLS-1$
}
}
String resources = att(taskEle, "Resources"); //$NON-NLS-1$
if (resources != null) {
String[] resourceArray = resources.split("; "); //$NON-NLS-1$
taskContent = ensureTaskContent(topic, taskContent);
taskContent.deleteChildren("assigned-to");//$NON-NLS-1$
ITopicExtensionElement ele = taskContent.createChild("assigned-to"); //$NON-NLS-1$
ele.setTextContent(resources);
for (String resource : resourceArray) {
topic.addLabel(NLS.bind(
ImportMessages.MindManagerImporter_ResourceLabel,
resource.replaceAll(",", ";"))); //$NON-NLS-1$//$NON-NLS-2$
}
}
String durationHours = att(taskEle, "DurationHours"); //$NON-NLS-1$
if (durationHours != null) {
String durationLabel = null;
String durationUnit = att(taskEle, "DurationUnit"); //$NON-NLS-1$
if (durationUnit != null) {
try {
int hours = Integer.parseInt(durationHours);
if ("urn:mindjet:Month".equals(durationUnit)) { //$NON-NLS-1$
durationLabel = NLS.bind(
ImportMessages.MindManagerImporter_Months,
hours / 160);
} else if ("urn:mindjet:Week".equals(durationUnit)) { //$NON-NLS-1$
durationLabel = NLS.bind(
ImportMessages.MindManagerImporter_Weeks,
hours / 40);
} else if ("urn:mindjet:Day".equals(durationUnit)) { //$NON-NLS-1$
durationLabel = NLS.bind(
ImportMessages.MindManagerImporter_Days,
hours / 8);
}
} catch (NumberFormatException e) {
}
}
if (durationLabel == null) {
durationLabel = NLS.bind(
ImportMessages.MindManagerImporter_Hours,
durationHours);
}
topic.addLabel(
NLS.bind(ImportMessages.MindManagerImporter_DurationLabel,
durationLabel));
}
addCheckPoint(ensureTaskContent(topic, taskContent),
att(taskEle, "Milestone")); //$NON-NLS-1$
}
private static ITopicExtensionElement ensureTaskContent(ITopic topic,
ITopicExtensionElement taskContent) {
if (taskContent != null)
return taskContent;
ITopicExtension ext = topic.createExtension("org.xmind.ui.taskInfo"); //$NON-NLS-1$
return ext.getContent();
}
private void loadTitle(Element ownerEle, ITitled titleOwner)
throws InterruptedException {
checkInterrupted();
Element textEle = child(ownerEle, "ap:Text"); //$NON-NLS-1$
String title = textEle == null ? null : att(textEle, "PlainText"); //$NON-NLS-1$
if (title == null && titleOwner instanceof ITopic) {
title = ImporterUtils.getDefaultTopicTitle((ITopic) titleOwner);
}
if (title != null) {
titleOwner.setTitleText(title);
}
if (textEle != null && titleOwner instanceof IStyled) {
Element fontEle = child(textEle, "ap:Font"); //$NON-NLS-1$
if (fontEle != null) {
loadFont(fontEle, (IStyled) titleOwner);
}
String align = textEle == null ? null
: att(textEle, "TextAlignment"); //$NON-NLS-1$
if (align != null) {
loadTextAlignment(align, (IStyled) titleOwner);
}
}
}
private void loadTextAlignment(String align, IStyled styleOwner) {
if (align.startsWith("urn:mindjet:")) { //$NON-NLS-1$
int len = "urn:mindjet:".length(); //$NON-NLS-1$
String textAlign = align.substring(len).toLowerCase();
registerStyle(styleOwner, Styles.TextAlign, textAlign);
}
}
private void loadFont(Element fontEle, IStyled styleOwner)
throws InterruptedException {
checkInterrupted();
registerStyle(styleOwner, Styles.TextColor,
parseColor(att(fontEle, "Color"))); //$NON-NLS-1$
String fontName = att(fontEle, "Name"); //$NON-NLS-1$
String availableFontName = FontUtils.getAAvailableFontNameFor(fontName);
fontName = availableFontName != null ? availableFontName : fontName;
registerStyle(styleOwner, Styles.FontFamily, fontName);
registerStyle(styleOwner, Styles.FontSize,
parseFontSize(att(fontEle, "Size"))); //$NON-NLS-1$
registerStyle(styleOwner, Styles.FontWeight,
Boolean.parseBoolean(att(fontEle, "Bold")) //$NON-NLS-1$
? Styles.FONT_WEIGHT_BOLD : null);
registerStyle(styleOwner, Styles.FontStyle,
Boolean.parseBoolean(att(fontEle, "Italic")) //$NON-NLS-1$
? Styles.FONT_STYLE_ITALIC : null);
String textDecoration = StyleUtils.toTextDecoration(
Boolean.parseBoolean(att(fontEle, "Underline")), Boolean //$NON-NLS-1$
.parseBoolean(att(fontEle, "Strikethrough"))); //$NON-NLS-1$
registerStyle(styleOwner, Styles.TextDecoration, textDecoration);
}
private static String parseFontSize(String size) {
if (size != null) {
try {
double value = Double.parseDouble(size);
size = StyleUtils.addUnitPoint((int) value);
} catch (Throwable e) {
}
}
return size;
}
private void loadColor(Element parentEle, IStyled host, boolean transparent)
throws InterruptedException {
checkInterrupted();
Element colorEle = child(parentEle, "ap:Color"); //$NON-NLS-1$
if (colorEle != null) {
String fillColor = att(colorEle, "FillColor"); //$NON-NLS-1$
String opacity = parseAlpha(fillColor);
if (opacity != null && !opacity.equals(TRANSPARENT_VALUE))
registerStyle(host, Styles.FillColor, parseColor(fillColor));
if (transparent) {
registerStyle(host, Styles.Opacity, opacity);
}
String lineColor = att(colorEle, "LineColor"); //$NON-NLS-1$
registerStyle(host, Styles.LineColor, parseColor(lineColor));
}
}
private IStyleSheet getTempStyleSheet() {
if (tempStyleSheet == null) {
tempStyleSheet = Core.getStyleSheetBuilder().createStyleSheet();
((StyleSheetImpl) tempStyleSheet)
.setManifest(getTargetWorkbook().getManifest());
}
return tempStyleSheet;
}
private void registerStyle(IStyled styleOwner, String key, String value) {
if (value == null)
return;
IStyle style = styleMap.get(styleOwner);
if (style == null) {
style = getTempStyleSheet().createStyle(styleOwner.getStyleType());
getTempStyleSheet().addStyle(style, IStyleSheet.NORMAL_STYLES);
styleMap.put(styleOwner, style);
}
if (Styles.TextDecoration.equals(key)) {
String oldValue = style.getProperty(key);
if (oldValue != null && !oldValue.contains(value)) {
boolean underline = oldValue
.contains(Styles.TEXT_DECORATION_UNDERLINE)
|| value.contains(Styles.TEXT_DECORATION_UNDERLINE);
boolean strikeout = oldValue
.contains(Styles.TEXT_DECORATION_LINE_THROUGH)
|| value.contains(Styles.TEXT_DECORATION_LINE_THROUGH);
value = StyleUtils.toTextDecoration(underline, strikeout);
}
}
style.setProperty(key, value);
}
private static String parseAlpha(String mmColor) {
if (mmColor != null) {
try {
int alpha = Integer.parseInt(mmColor.substring(0, 1), 16);
double opacity = ((double) alpha) * 100 / 255;
return String.format("%.2f", opacity); //$NON-NLS-1$
} catch (Throwable t) {
}
}
return null;
}
private static String parseColor(String mmColor) {
if (mmColor != null) {
int r;
int g;
int b;
try {
r = Integer.parseInt(mmColor.substring(2, 4), 16);
g = Integer.parseInt(mmColor.substring(4, 6), 16);
b = Integer.parseInt(mmColor.substring(6, 8), 16);
return ColorUtils.toString(r, g, b);
} catch (Throwable t) {
}
}
return null;
}
private void loadOId(Element mmEle, IIdentifiable element) {
String OId = att(mmEle, "OId"); //$NON-NLS-1$
if (OId != null) {
idMap.put(OId, element.getId());
}
}
private void recordTopicLink(String OId, ITopic sourceTopic) {
List<ITopic> topics = topicLinkMap.get(OId);
if (topics == null) {
topics = new ArrayList<ITopic>();
topicLinkMap.put(OId, topics);
}
topics.add(sourceTopic);
}
private void setTopicLinks() {
for (Entry<String, List<ITopic>> en : topicLinkMap.entrySet()) {
String id = idMap.get(en.getKey());
if (id != null) {
for (ITopic topic : en.getValue()) {
topic.setHyperlink(HyperlinkUtils.toInternalURL(id));
}
}
}
}
private static Element child(Element parentEle, String childTag) {
return children(parentEle, childTag).next();
}
private static Iterator<Element> children(final Element parentEle,
final String childTag) {
return new Iterator<Element>() {
String tag = DOMUtils.getLocalName(childTag);
Iterator<Element> it = DOMUtils.childElementIter(parentEle);
Element next = findNext();
public void remove() {
}
private Element findNext() {
while (it.hasNext()) {
Element ele = it.next();
if (DOMUtils.getLocalName(ele.getTagName())
.equalsIgnoreCase(tag)) {
return ele;
}
}
return null;
}
public Element next() {
Element result = next;
next = findNext();
return result;
}
public boolean hasNext() {
return next != null;
}
};
}
private static String att(Element ele, String attName) {
if (ele.hasAttribute(attName))
return ele.getAttribute(attName);
attName = DOMUtils.getLocalName(attName);
NamedNodeMap atts = ele.getAttributes();
for (int i = 0; i < atts.getLength(); i++) {
Node att = atts.item(i);
if (attName.equalsIgnoreCase(
DOMUtils.getLocalName(att.getNodeName()))) {
return att.getNodeValue();
}
}
return null;
}
private static int mm2Dots(float mm) {
return (int) (mm * DPM);
}
private static String getMapping(String type, String sourceId,
String defaultId) {
if (sourceId != null) {
String destination = getMappings().getDestination(type, sourceId);
if (destination != null)
return destination;
}
return defaultId;
}
private static ResourceMappingManager getMappings() {
if (mappings == null) {
mappings = createMappings();
}
return mappings;
}
private static ResourceMappingManager createMappings() {
return MMResourceMappingManager.getInstance();
}
private static DocumentBuilder getDocumentBuilder()
throws ParserConfigurationException {
return DOMUtils.getDefaultDocumentBuilder();
}
public void error(SAXParseException exception) throws SAXException {
log(exception, null);
}
public void fatalError(SAXParseException exception) throws SAXException {
log(exception, null);
}
public void warning(SAXParseException exception) throws SAXException {
log(exception, null);
}
}