/* ******************************************************************************
* 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.core.internal.dom;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_END1;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_END2;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_HREF;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_ID;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_MARKER_ID;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_RESOURCE_ID;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_SRC;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_STYLE_ID;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_THEME;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_TOPIC_ID;
import static org.xmind.core.internal.dom.DOMConstants.TAG_IMG;
import static org.xmind.core.internal.dom.DOMConstants.TAG_MARKER_REF;
import static org.xmind.core.internal.dom.DOMConstants.TAG_PROPERTIES;
import static org.xmind.core.internal.dom.DOMConstants.TAG_RELATIONSHIP;
import static org.xmind.core.internal.dom.DOMConstants.TAG_RESOURCE_REF;
import static org.xmind.core.internal.dom.DOMConstants.TAG_SUMMARY;
import static org.xmind.core.internal.dom.DOMConstants.TAG_TOPIC;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xmind.core.Core;
import org.xmind.core.IAdaptable;
import org.xmind.core.ICloneData;
import org.xmind.core.IFileEntry;
import org.xmind.core.IImage;
import org.xmind.core.IManifest;
import org.xmind.core.IResourceRef;
import org.xmind.core.ITopic;
import org.xmind.core.IWorkbook;
import org.xmind.core.IWorkbookComponent;
import org.xmind.core.internal.CloneData;
import org.xmind.core.internal.ICloneDataListener;
import org.xmind.core.internal.zip.ArchiveConstants;
import org.xmind.core.marker.IMarker;
import org.xmind.core.marker.IMarkerGroup;
import org.xmind.core.marker.IMarkerResource;
import org.xmind.core.marker.IMarkerSheet;
import org.xmind.core.style.IStyle;
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.core.util.Property;
public class WorkbookUtilsImpl {
private WorkbookUtilsImpl() {
}
public static ICloneData clone(IWorkbook targetWorkbook,
Collection<? extends Object> sources, ICloneData prevResult) {
CloneData result = new CloneData(sources, prevResult);
for (Object source : sources) {
if (result.get(source) == null) {
Object cloned;
if (source instanceof IImage) {
cloned = doClone((WorkbookImpl) targetWorkbook,
((IImage) source).getParent(), result);
if (cloned instanceof ITopic) {
cloned = ((ITopic) cloned).getImage();
}
} else {
cloned = doClone((WorkbookImpl) targetWorkbook, source,
result);
}
if (cloned != null) {
result.put(source, cloned);
}
}
}
return result;
}
private static Object doClone(WorkbookImpl targetWorkbook, Object source,
CloneData data) {
if (source instanceof IAdaptable) {
if (source instanceof IWorkbookComponent) {
Node sourceNode = (Node) ((IAdaptable) source)
.getAdapter(Node.class);
if (sourceNode != null) {
return cloneWorkbookComponent(targetWorkbook, sourceNode,
(IWorkbookComponent) source, data);
}
} else if (source instanceof IMarker) {
IMarker sourceMarker = (IMarker) source;
return cloneMarker(targetWorkbook,
(MarkerSheetImpl) targetWorkbook.getMarkerSheet(),
sourceMarker.getId(), sourceMarker,
sourceMarker.getOwnedSheet(), data);
}
}
return null;
}
private static Object cloneWorkbookComponent(WorkbookImpl targetWorkbook,
Node sourceEle, IWorkbookComponent source, CloneData data) {
Document doc = targetWorkbook.getImplementation();
Node clonedEle = clone(doc, sourceEle);
if (clonedEle instanceof Element) {
replaceAttributes(targetWorkbook, (Element) clonedEle,
source.getOwnedWorkbook(), data);
}
return targetWorkbook.getAdaptableRegistry().getAdaptable(clonedEle);
}
private static Node clone(Document doc, Node source) {
if (source.getOwnerDocument() == doc) {
return source.cloneNode(true);
}
return doc.importNode(source, true);
}
private static void replaceAttributes(WorkbookImpl targetWorkbook,
Element ele, IWorkbook sourceWorkbook, CloneData data) {
if (ele.hasAttribute(ATTR_ID)) {
replaceId(ele, data, ICloneData.WORKBOOK_COMPONENTS);
}
for (Iterator<Element> it = DOMUtils.childElementIter(ele); it
.hasNext();) {
replaceAttributes(targetWorkbook, it.next(), sourceWorkbook, data);
}
String tag = ele.getTagName();
if (TAG_SUMMARY.equals(tag)) {
replaceTopicRef(ele, ATTR_TOPIC_ID, data);
} else if (TAG_RELATIONSHIP.equals(tag)) {
replaceTopicRef(ele, ATTR_END1, data);
replaceTopicRef(ele, ATTR_END2, data);
} else if (TAG_TOPIC.equals(tag)) {
replaceTopicHyperlink(targetWorkbook, ele, sourceWorkbook, data);
//replaceTopicImageUrl(targetWorkbook, ele, sourceWorkbook, data);
//replaceNotes(targetWorkbook, ele, sourceWorkbook, data);
} else if (TAG_MARKER_REF.equals(tag)) {
replaceMarkerRef(targetWorkbook, ele, sourceWorkbook, data);
} else if (TAG_IMG.equals(tag)) {
replaceImageUrl(targetWorkbook, ele, sourceWorkbook, data);
} else if (TAG_RESOURCE_REF.equals(tag)) {
replaceResourceId(targetWorkbook, ele, sourceWorkbook, data);
}
if (ele.hasAttribute(ATTR_STYLE_ID)) {
replaceStyle(targetWorkbook, ele, ATTR_STYLE_ID, sourceWorkbook,
data);
}
if (ele.hasAttribute(ATTR_THEME)) {
replaceStyle(targetWorkbook, ele, ATTR_THEME, sourceWorkbook, data);
}
}
private static void replaceResourceId(WorkbookImpl targetWorkbook,
Element ele, IWorkbook sourceWorkbook, CloneData data) {
String type = DOMUtils.getAttribute(ele, DOMConstants.ATTR_TYPE);
if (IResourceRef.FILE_ENTRY.equals(type)) {
String sourceEntryPath = DOMUtils.getAttribute(ele,
ATTR_RESOURCE_ID);
if (sourceEntryPath != null) {
try {
String targetEntryPath = InternalHyperlinkUtils
.importAttachment(sourceEntryPath, sourceWorkbook,
targetWorkbook);
DOMUtils.setAttribute(ele, ATTR_RESOURCE_ID,
targetEntryPath);
} catch (IOException e) {
}
}
}
}
private static void replaceId(Element ele, CloneData data, String category) {
String oldId = DOMUtils.getAttribute(ele, ATTR_ID);
String newId = Core.getIdFactory().createId();
DOMUtils.replaceId(ele, newId);
if (oldId != null && data.getString(category, oldId) == null) {
data.putString(category, oldId, newId);
}
}
private static void replaceTopicRef(final Element e, final String attrName,
final CloneData data) {
String oldRefId = DOMUtils.getAttribute(e, attrName);
if (oldRefId == null)
return;
String newRefId = data.getString(ICloneData.WORKBOOK_COMPONENTS,
oldRefId);
if (newRefId == null) {
data.addCloneDataListener(ICloneData.WORKBOOK_COMPONENTS, oldRefId,
new ICloneDataListener() {
public void objectCloned(Object source, Object cloned) {
}
public void stringCloned(String category,
String source, String cloned) {
DOMUtils.setAttribute(e, attrName, cloned);
data.removeCloneDataListener(source, this);
}
});
} else {
DOMUtils.setAttribute(e, attrName, newRefId);
}
}
// private static void replaceStyle(WorkbookImpl targetWorkbook,
// final Element styledEle, IWorkbook sourceWorkbook,
// final CloneData data) {
// replaceStyle(targetWorkbook, styledEle, ATTR_STYLE_ID, sourceWorkbook,
// data);
// }
//
private static void replaceStyle(WorkbookImpl targetWorkbook,
final Element styledEle, String styleTag, IWorkbook sourceWorkbook,
final CloneData data) {
String styleId = styledEle.getAttribute(styleTag);
IStyle sourceStyle = sourceWorkbook.getStyleSheet().findStyle(styleId);
String sourceStyleId = sourceStyle == null ? null : sourceStyle.getId();
String targetStyleId;
if (sourceStyle != null) {
IStyle targetStyle = importStyle(
(StyleSheetImpl) targetWorkbook.getStyleSheet(),
(StyleImpl) sourceStyle,
(StyleSheetImpl) sourceWorkbook.getStyleSheet(), data);
targetStyleId = targetStyle == null ? null : targetStyle.getId();
} else {
IStyle targetStyle = (IStyle) data.get(sourceStyle);
targetStyleId = targetStyle == null ? null : targetStyle.getId();
}
if (targetStyleId == null) {
styledEle.removeAttribute(styleTag);
} else {
styledEle.setAttribute(styleTag, targetStyleId);
}
data.putString(ICloneData.STYLESHEET_COMPONENTS, sourceStyleId,
targetStyleId);
// String oldStyleId = DOMUtils.getAttribute(styledEle, styleTag);
// if (oldStyleId == null)
// return;
//
// String newStyleId = data.getString(oldStyleId);
// if (newStyleId != null || data.isCloned(oldStyleId)) {
// DOMUtils.setAttribute(styledEle, styleTag, newStyleId);
// return;
// }
//
// IStyleSheet sourceStyleSheet = sourceWorkbook.getStyleSheet();
// IStyle sourceStyle = sourceStyleSheet.findStyle(oldStyleId);
// if (sourceStyle != null) {
// Properties sourceContents = getCachedSourceStyleContents(
// sourceStyle, targetWorkbook, sourceWorkbook, data);
// if (!sourceContents.isEmpty()) {
// IStyleSheet targetStyleSheet = targetWorkbook.getStyleSheet();
// IStyle targetStyle = findSimilarStyle(sourceContents,
// targetStyleSheet, data);
// if (targetStyle == null) {
// targetStyle = createStyle(sourceContents, sourceStyle
// .getType(), targetStyleSheet, targetWorkbook,
// sourceWorkbook, data);
// }
// newStyleId = targetStyle.getId();
// }
// }
// DOMUtils.setAttribute(styledEle, styleTag, newStyleId);
// data.put(oldStyleId, newStyleId);
}
// private static IStyle createStyle(Properties contents, String styleType,
// IStyleSheet targetStyleSheet, WorkbookImpl targetWorkbook,
// IWorkbook sourceWorkbook, CloneData data) {
// IStyle newStyle = targetStyleSheet.createStyle(styleType);
// Enumeration<?> keys = contents.propertyNames();
// while (keys.hasMoreElements()) {
// String key = (String) keys.nextElement();
// String value = contents.getProperty(key);
// if (HyperlinkUtils.isAttachmentURL(value)) {
// value = cloneUrl(targetWorkbook, value, sourceWorkbook, data);
// }
// newStyle.setProperty(key, value);
// }
// targetStyleSheet.addStyle(newStyle, IStyleSheet.NORMAL_STYLES);
// return newStyle;
// }
//
// private static IStyle findSimilarStyle(Properties sourceContents,
// IStyleSheet targetStyleSheet, CloneData data) {
// for (IStyle style : targetStyleSheet.getAllStyles()) {
// Properties targetContents = getCachedStyleContents(style, data);
// if (targetContents.equals(sourceContents))
// return style;
// }
// return null;
// }
//
// private static Properties getCachedSourceStyleContents(IStyle style,
// WorkbookImpl targetWorkbook, IWorkbook sourceWorkbook,
// CloneData data) {
// Properties map = (Properties) data.getCache(style);
// if (map == null) {
// map = new Properties();
// Iterator<Property> it = style.properties();
// while (it.hasNext()) {
// Property p = it.next();
// map.setProperty(p.key, p.value);
// }
// data.cache(style, map);
// }
// return map;
// }
//
// private static Properties getCachedStyleContents(IStyle style,
// CloneData data) {
// Properties map = (Properties) data.getCache(style);
// if (map == null) {
// map = new Properties();
// Iterator<Property> it = style.properties();
// while (it.hasNext()) {
// Property p = it.next();
// map.setProperty(p.key, p.value);
// }
// data.cache(style, map);
// }
// return map;
// }
// private static String cloneUrl(WorkbookImpl targetWorkbook,
// String sourceUrl, IWorkbook sourceWorkbook, CloneData data) {
// String clonedUrl = data.getString(sourceUrl);
// if (clonedUrl != null || data.isCloned(sourceUrl))
// return clonedUrl;
//
// if (HyperlinkUtils.isAttachmentURL(sourceUrl)) {
// try {
// clonedUrl = InternalHyperlinkUtils.importAttachmentURL(
// sourceUrl, sourceWorkbook, targetWorkbook);
// } catch (IOException e) {
// }
// } else if (HyperlinkUtils.isInternalURL(sourceUrl)) {
// String sourceId = HyperlinkUtils.toElementID(sourceUrl);
//
// } else {
// clonedUrl = sourceUrl;
// }
//
// data.put(sourceUrl, clonedUrl);
// return clonedUrl;
// }
private static void replaceHyperlink(WorkbookImpl targetWorkbook,
final Element ele, final String attr, IWorkbook sourceWorkbook,
final CloneData data) {
final String sourceUrl = DOMUtils.getAttribute(ele, attr);
if (sourceUrl != null) {
String clonedUrl = data.getString(ICloneData.URLS, sourceUrl);
boolean async = false;
if (clonedUrl == null && !data.isCloned(ICloneData.URLS, sourceUrl)) {
if (HyperlinkUtils.isAttachmentURL(sourceUrl)) {
try {
clonedUrl = InternalHyperlinkUtils.importAttachmentURL(
sourceUrl, sourceWorkbook, targetWorkbook);
} catch (IOException e) {
}
} else if (HyperlinkUtils.isInternalURL(sourceUrl)) {
String sourceId = HyperlinkUtils.toElementID(sourceUrl);
if (!data
.isCloned(ICloneData.WORKBOOK_COMPONENTS, sourceId)) {
async = true;
data.addCloneDataListener(
ICloneData.WORKBOOK_COMPONENTS, sourceId,
new ICloneDataListener() {
public void objectCloned(Object source,
Object cloned) {
}
public void stringCloned(String category,
String source, String cloned) {
String targetUrl = cloned == null ? null
: HyperlinkUtils
.toInternalURL(cloned);
data.putString(ICloneData.URLS,
sourceUrl, targetUrl);
DOMUtils.setAttribute(ele, attr,
targetUrl);
data.removeCloneDataListener(source,
this);
}
});
}
String targetId = data.getString(
ICloneData.WORKBOOK_COMPONENTS, sourceId);
clonedUrl = HyperlinkUtils.toInternalURL(targetId);
} else {
clonedUrl = sourceUrl;
}
if (!async)
data.putString(ICloneData.URLS, sourceUrl, clonedUrl);
}
if (!async)
DOMUtils.setAttribute(ele, attr, clonedUrl);
}
}
private static void replaceTopicHyperlink(WorkbookImpl targetWorkbook,
Element topicEle, IWorkbook sourceWorkbook, CloneData data) {
replaceHyperlink(targetWorkbook, topicEle, ATTR_HREF, sourceWorkbook,
data);
// String oldUrl = DOMUtils.getAttribute(topicEle, ATTR_HREF);
// if (oldUrl != null) {
// String newUrl = cloneUrl(targetWorkbook, oldUrl, sourceWorkbook,
// data);
// DOMUtils.setAttribute(topicEle, ATTR_HREF, newUrl);
// }
}
private static void replaceImageUrl(WorkbookImpl targetWorkbook,
Element imgEle, IWorkbook sourceWorkbook, CloneData data) {
replaceHyperlink(targetWorkbook, imgEle, ATTR_SRC, sourceWorkbook, data);
// String oldUrl = DOMUtils.getAttribute(imgEle, ATTR_SRC);
// if (oldUrl != null) {
// String newUrl = cloneUrl(targetWorkbook, oldUrl, sourceWorkbook,
// data);
// DOMUtils.setAttribute(imgEle, ATTR_SRC, newUrl);
// }
}
private static void replaceMarkerRef(WorkbookImpl targetWorkbook,
Element ele, IWorkbook sourceWorkbook, CloneData data) {
String oldMarkerId = DOMUtils.getAttribute(ele, ATTR_MARKER_ID);
if (oldMarkerId != null) {
String newMarkerId = cloneMarkerId(targetWorkbook, oldMarkerId,
sourceWorkbook, data);
DOMUtils.setAttribute(ele, ATTR_MARKER_ID, newMarkerId);
}
}
private static String cloneMarkerId(WorkbookImpl targetWorkbook,
String sourceMarkerId, IWorkbook sourceWorkbook, CloneData data) {
String clonedMarkerId = data.getString(
ICloneData.MARKERSHEET_COMPONENTS, sourceMarkerId);
if (clonedMarkerId != null
|| data.isCloned(ICloneData.MARKERSHEET_COMPONENTS,
sourceMarkerId))
return clonedMarkerId;
IMarkerSheet sourceMarkerSheet = sourceWorkbook.getMarkerSheet();
IMarker sourceMarker = sourceMarkerSheet.findMarker(sourceMarkerId);
if (sourceMarker != null) {
MarkerSheetImpl targetMarkerSheet = (MarkerSheetImpl) targetWorkbook
.getMarkerSheet();
IMarker existingMarker = targetMarkerSheet
.findMarker(sourceMarkerId);
if (existingMarker == null) {
IMarker clonedMarker = cloneMarker(targetWorkbook,
targetMarkerSheet, sourceMarkerId, sourceMarker,
sourceMarkerSheet, data);
if (clonedMarker != null) {
clonedMarkerId = clonedMarker.getId();
}
}
}
if (clonedMarkerId == null) {
clonedMarkerId = sourceMarkerId;
}
data.putString(ICloneData.MARKERSHEET_COMPONENTS, sourceMarkerId,
clonedMarkerId);
return clonedMarkerId;
}
private static IMarker cloneMarker(WorkbookImpl targetWorkbook,
MarkerSheetImpl targetMarkerSheet, String sourceMarkerId,
IMarker sourceMarker, IMarkerSheet sourceMarkerSheet, CloneData data) {
if (!sourceMarkerSheet.isPermanent()) {
IMarkerGroup sourceGroup = sourceMarker.getParent();
if (sourceGroup != null) {
String sourceGroupId = sourceGroup.getId();
String clonedGroupId = data.getString(
ICloneData.MARKERSHEET_COMPONENTS, sourceGroupId);
if (clonedGroupId == null
&& !data.isCloned(ICloneData.MARKERSHEET_COMPONENTS,
sourceGroupId)) {
IMarkerGroup targetGroup = targetMarkerSheet
.findMarkerGroup(sourceGroupId);
if (targetGroup != null
&& targetMarkerSheet.equals(targetGroup
.getOwnedSheet())) {
data.putString(ICloneData.MARKERSHEET_COMPONENTS,
sourceGroupId, sourceGroupId);
} else {
cloneMarkerGroup(targetWorkbook, targetMarkerSheet,
sourceMarker, sourceGroup, sourceMarkerSheet,
data);
}
String clonedMarkerId = data.getString(
ICloneData.MARKERSHEET_COMPONENTS, sourceMarkerId);
if (clonedMarkerId != null) {
return targetMarkerSheet.findMarker(clonedMarkerId);
}
IMarker targetMarker = targetMarkerSheet
.findMarker(sourceMarkerId);
if (targetMarker == null) {
//TODO clone missing marker
}
}
}
}
return sourceMarker;
}
private static void cloneMarkerGroup(WorkbookImpl targetWorkbook,
MarkerSheetImpl targetMarkerSheet, IMarker sourceMarker,
IMarkerGroup sourceGroup, IMarkerSheet sourceMarkerSheet,
CloneData data) {
MarkerGroupImpl targetGroup;
IMarkerGroup existingGroup = targetMarkerSheet
.findMarkerGroup(sourceGroup.getId());
if (existingGroup != null
&& targetMarkerSheet.equals(existingGroup.getOwnedSheet())) {
targetGroup = (MarkerGroupImpl) existingGroup;
//TODO clone ungrouped markers
} else {
Node sourceGroupNode = (Node) sourceGroup.getAdapter(Node.class);
if (sourceGroupNode != null) {
Node clonedGroupNode = targetMarkerSheet.getImplementation()
.importNode(sourceGroupNode, true);
replaceMarkerPath(targetWorkbook, targetMarkerSheet,
clonedGroupNode, data);
MarkerGroupImpl clonedGroup = (MarkerGroupImpl) targetMarkerSheet
.getElementAdapter(clonedGroupNode);
transferMarkerResources(targetWorkbook, targetMarkerSheet,
clonedGroup, sourceGroup, sourceMarkerSheet, data);
targetGroup = clonedGroup;
} else {
targetGroup = (MarkerGroupImpl) targetMarkerSheet
.createMarkerGroup(sourceGroup.isSingleton());
cloneMarkerGroup(targetWorkbook, targetMarkerSheet,
targetGroup, sourceGroup, sourceMarkerSheet, data);
}
}
data.putString(ICloneData.MARKERSHEET_COMPONENTS, sourceGroup.getId(),
targetGroup.getId());
}
private static void transferMarkerResources(WorkbookImpl targetWorkbook,
MarkerSheetImpl targetMarkerSheet, MarkerGroupImpl targetGroup,
IMarkerGroup sourceGroup, IMarkerSheet sourceMarkerSheet,
CloneData data) {
for (IMarker targetMarker : targetGroup.getMarkers()) {
String markerId = targetMarker.getId();
IMarker sourceMarker = sourceMarkerSheet.findMarker(markerId);
if (sourceMarker != null) {
transferMarkerResource(targetWorkbook, sourceMarker,
targetMarker);
}
data.putString(ICloneData.MARKERSHEET_COMPONENTS, markerId,
markerId);
}
}
private static void cloneMarkerGroup(WorkbookImpl targetWorkbook,
MarkerSheetImpl targetMarkerSheet, MarkerGroupImpl targetGroup,
IMarkerGroup sourceGroup, IMarkerSheet sourceMarkerSheet,
CloneData data) {
for (IMarker sourceMarker : sourceGroup.getMarkers()) {
IMarker clonedMarker;
String sourceMarkerId = sourceMarker.getId();
IMarker existingMarker = targetMarkerSheet
.findMarker(sourceMarkerId);
if (existingMarker != null
&& targetMarkerSheet.equals(existingMarker.getOwnedSheet())) {
clonedMarker = existingMarker;
} else {
clonedMarker = cloneMarker(targetWorkbook, targetMarkerSheet,
sourceMarkerSheet, sourceMarker, data);
targetGroup.addMarker(clonedMarker);
}
data.putString(ICloneData.MARKERSHEET_COMPONENTS, sourceMarkerId,
clonedMarker.getId());
}
}
private static IMarker cloneMarker(WorkbookImpl targetWorkbook,
MarkerSheetImpl targetMarkerSheet, IMarkerSheet sourceMarkerSheet,
IMarker sourceMarker, CloneData data) {
IMarker clonedMarker;
Node sourceMarkerNode = (Node) sourceMarker.getAdapter(Node.class);
if (sourceMarkerNode != null) {
Node clonedMarkerNode = targetMarkerSheet.getImplementation()
.importNode(sourceMarkerNode, true);
replaceMarkerPath(targetWorkbook, targetMarkerSheet,
clonedMarkerNode, data);
clonedMarker = (IMarker) targetMarkerSheet
.getElementAdapter(clonedMarkerNode);
} else {
clonedMarker = createSimilarMarker(targetWorkbook,
targetMarkerSheet, sourceMarker, sourceMarkerSheet, data);
}
transferMarkerResource(targetWorkbook, sourceMarker, clonedMarker);
return clonedMarker;
}
private static void transferMarkerResource(WorkbookImpl targetWorkbook,
IMarker sourceMarker, IMarker targetMarker) {
IMarkerResource sourceResource = sourceMarker.getResource();
if (sourceResource != null) {
//IMarkerResource targetResource = targetMarker.getResource();
InputStream in = sourceResource.getInputStream();
if (in != null) {
String targetPath = ArchiveConstants.PATH_MARKERS
+ targetMarker.getResourcePath();
IFileEntry entry = targetWorkbook.getManifest()
.createFileEntry(targetPath);
OutputStream out = entry.getOutputStream();
if (out != null) {
try {
FileUtils.transfer(in, out, true);
} catch (IOException e) {
}
}
}
}
}
private static void replaceMarkerPath(WorkbookImpl targetWorkbook,
MarkerSheetImpl targetMarkerSheet, Node node, CloneData data) {
if (node instanceof Element) {
Element ele = (Element) node;
if (DOMConstants.TAG_MARKER.equals(ele.getTagName())) {
String clonedPath = createNewMarkerPath(ele
.getAttribute(DOMConstants.ATTR_RESOURCE));
ele.setAttribute(DOMConstants.ATTR_RESOURCE, clonedPath);
}
Iterator<Element> it = DOMUtils.childElementIter(ele);
while (it.hasNext()) {
replaceMarkerPath(targetWorkbook, targetMarkerSheet, it.next(),
data);
}
}
}
private static String createNewMarkerPath(String sourcePath) {
String clonedPath = Core.getIdFactory().createId()
+ FileUtils.getExtension(sourcePath);
return clonedPath;
}
private static MarkerImpl createSimilarMarker(WorkbookImpl targetWorkbook,
MarkerSheetImpl targetMarkerSheet, IMarker sourceMarker,
IMarkerSheet sourceMarkerSheet, CloneData data) {
MarkerImpl newMarker = (MarkerImpl) targetMarkerSheet
.createMarker(createNewMarkerPath(sourceMarker
.getResourcePath()));
newMarker.setName(sourceMarker.getName());
return newMarker;
}
public static void increaseStyleRef(WorkbookImpl workbook, IStyled styled) {
if (workbook == null || styled == null)
return;
String styleId = styled.getStyleId();
if (styleId == null)
return;
workbook.getStyleRefCounter().increaseRef(styleId);
}
public static void decreaseStyleRef(WorkbookImpl workbook, IStyled styled) {
if (workbook == null || styled == null)
return;
String styleId = styled.getStyleId();
if (styleId == null)
return;
workbook.getStyleRefCounter().decreaseRef(styleId);
}
public static IStyle importStyle(StyleSheetImpl targetSheet,
StyleImpl sourceStyle, StyleSheetImpl sourceSheet) {
return importStyle(targetSheet, sourceStyle, sourceSheet,
new CloneData(Arrays.asList(sourceStyle), null));
}
public static IStyle importStyle(StyleSheetImpl targetSheet,
StyleImpl sourceStyle, StyleSheetImpl sourceSheet, CloneData data) {
if (sourceSheet != null && sourceSheet.equals(targetSheet))
return sourceStyle;
IStyle targetStyle = targetSheet.findStyle(sourceStyle.getId());
if (targetStyle != null)
return targetStyle;
if (sourceSheet == null)
return importNoParentStyle(targetSheet, sourceStyle);
if (data == null)
data = new CloneData(Arrays.asList(sourceStyle), null);
StyleProperties sourceProp = getStyleProperties(sourceStyle, data);
if (sourceProp.isEmpty())
return null;
String sourceGroup = sourceSheet.findOwnedGroup(sourceStyle);
targetStyle = findSimilarStyle(targetSheet, sourceGroup, sourceProp,
data);
if (targetStyle != null)
return targetStyle;
cloneStyle(targetSheet, sourceStyle, sourceSheet, data);
targetStyle = (IStyle) data.get(sourceStyle);
if (targetStyle != null && sourceGroup != null) {
targetSheet.addStyle(targetStyle, sourceGroup);
}
return targetStyle;
}
private static IStyle findSimilarStyle(StyleSheetImpl targetSheet,
String group, StyleProperties sourceProp, CloneData data) {
Set<IStyle> styles;
if (group == null)
styles = targetSheet.getAllStyles();
else
styles = targetSheet.getStyles(group);
for (IStyle style : styles) {
if (sourceProp.equals(getStyleProperties(style, data)))
return style;
}
return null;
}
private static class StyleProperties {
Map<String, String> properties = new HashMap<String, String>();
Map<String, StyleProperties> defaultStyles = new HashMap<String, StyleProperties>();
public StyleProperties(IStyle style, CloneData data) {
Iterator<Property> propIt = style.properties();
while (propIt.hasNext()) {
Property next = propIt.next();
properties.put(next.key, next.value);
}
Iterator<Property> dsIt = style.defaultStyles();
while (dsIt.hasNext()) {
Property next = dsIt.next();
String family = next.key;
IStyle ds = style.getDefaultStyleById(next.value);
StyleProperties dsProp = (StyleProperties) data.getCache(ds);
if (dsProp == null) {
dsProp = new StyleProperties(ds, data);
}
defaultStyles.put(family, dsProp);
}
data.cache(style, this);
}
public boolean isEmpty() {
return properties.isEmpty() && defaultStyles.isEmpty();
}
public int hashCode() {
return properties.hashCode() ^ defaultStyles.hashCode();
}
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || !(obj instanceof StyleProperties))
return false;
StyleProperties that = (StyleProperties) obj;
return this.properties.equals(that.properties)
&& this.defaultStyles.equals(that.defaultStyles);
}
}
private static StyleProperties getStyleProperties(IStyle style,
CloneData data) {
StyleProperties prop = (StyleProperties) data.getCache(style);
if (prop == null) {
prop = new StyleProperties(style, data);
}
return prop;
}
private static void cloneStyle(StyleSheetImpl targetSheet,
StyleImpl sourceStyle, StyleSheetImpl sourceSheet, CloneData data) {
Element sourceEle = sourceStyle.getImplementation();
Node targetEle = clone(targetSheet.getImplementation(), sourceEle);
if (targetEle instanceof Element) {
replaceStyleProperties(targetSheet, (Element) targetEle, sourceEle,
sourceStyle, sourceSheet, data);
}
IStyle targetStyle = (IStyle) targetSheet.getNodeAdaptable(targetEle);
data.put(sourceStyle, targetStyle);
}
private static void replaceStyleProperties(StyleSheetImpl targetSheet,
Element targetEle, Element sourceEle, StyleImpl sourceStyle,
StyleSheetImpl sourceSheet, CloneData data) {
String type = targetEle.getAttribute(DOMConstants.ATTR_TYPE)
.toLowerCase();
String propTagName = type + "-" + TAG_PROPERTIES; //$NON-NLS-1$
Iterator<Element> targetPropIt = DOMUtils.childElementIterByTag(
targetEle, propTagName);
while (targetPropIt.hasNext()) {
Element targetPropEle = targetPropIt.next();
NamedNodeMap attrs = targetPropEle.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
Node attr = attrs.item(i);
// String key = attr.getNodeName();
String value = attr.getNodeValue();
if (HyperlinkUtils.isAttachmentURL(value)) {
String newValue = cloneAttachment(value,
sourceSheet.getManifest(),
targetSheet.getManifest(), data);
attr.setNodeValue(newValue);
}
}
Iterator<Element> targetDSIt = DOMUtils.childElementIterByTag(
targetPropEle, DOMConstants.TAG_DEFAULT_STYLE);
while (targetDSIt.hasNext()) {
Element targetDSEle = targetDSIt.next();
String family = DOMUtils.getAttribute(targetDSEle,
DOMConstants.ATTR_STYLE_FAMILY);
if (family != null) {
String dsId = DOMUtils.getAttribute(targetDSEle,
DOMConstants.ATTR_STYLE_ID);
IStyle sourceDS = sourceStyle.getDefaultStyleById(dsId);
if (sourceDS != null) {
IStyle targetDS = importStyle(targetSheet,
(StyleImpl) sourceDS, sourceSheet, data);
if (targetDS != null) {
DOMUtils.setAttribute(targetDSEle, ATTR_STYLE_ID,
targetDS.getId());
}
}
}
}
}
}
private static String cloneAttachment(String sourceURL,
IManifest sourceManifest, IManifest targetManifest, CloneData data) {
String targetURL = data.getString(ICloneData.URLS, sourceURL);
if (targetURL != null)
return targetURL;
if (sourceManifest == null || targetManifest == null) {
return (String) cache(data, sourceURL, sourceURL);
}
IFileEntry sourceEntry = sourceManifest.getFileEntry(HyperlinkUtils
.toAttachmentPath(sourceURL));
if (sourceEntry == null)
return (String) cache(data, sourceURL, sourceURL);
String newPath = Core.getIdFactory().createId()
+ FileUtils.getExtension(sourceEntry.getPath());
String attachmentPath = targetManifest.makeAttachmentPath(newPath);
String mediaType = sourceEntry.getMediaType();
IFileEntry targetEntry = targetManifest.createFileEntry(attachmentPath,
mediaType);
targetEntry.increaseReference();
InputStream is = sourceEntry.getInputStream();
OutputStream os = targetEntry.getOutputStream();
if (is != null && os != null) {
try {
FileUtils.transfer(is, os, true);
} catch (IOException e) {
} finally {
try {
is.close();
} catch (IOException e1) {
}
try {
os.close();
} catch (IOException e) {
}
}
}
targetURL = HyperlinkUtils.toAttachmentURL(targetEntry.getPath());
return (String) cache(data, sourceURL, targetURL);
}
private static Object cache(CloneData data, Object source, Object target) {
data.cache(source, target);
return target;
}
private static IStyle importNoParentStyle(StyleSheetImpl targetSheet,
StyleImpl sourceStyle) {
Element sourceEle = sourceStyle.getImplementation();
Node targetEle = clone(targetSheet.getImplementation(), sourceEle);
return (IStyle) targetSheet.getNodeAdaptable(targetEle);
}
}