/* ******************************************************************************
* 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_RESOURCE;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_SINGLETON;
import static org.xmind.core.internal.dom.DOMConstants.TAG_MARKER;
import static org.xmind.core.internal.dom.DOMConstants.TAG_MARKER_GROUP;
import static org.xmind.core.internal.dom.DOMConstants.TAG_MARKER_SHEET;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipFile;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xmind.core.Core;
import org.xmind.core.CoreException;
import org.xmind.core.IAdaptable;
import org.xmind.core.IManifest;
import org.xmind.core.event.ICoreEventListener;
import org.xmind.core.event.ICoreEventRegistration;
import org.xmind.core.event.ICoreEventSource;
import org.xmind.core.event.ICoreEventSupport;
import org.xmind.core.internal.ElementRegistry;
import org.xmind.core.internal.MarkerSheet;
import org.xmind.core.internal.event.CoreEventSupport;
import org.xmind.core.internal.zip.ArchiveConstants;
import org.xmind.core.internal.zip.ZipFileInputSource;
import org.xmind.core.io.DirectoryInputSource;
import org.xmind.core.io.IInputSource;
import org.xmind.core.marker.IMarker;
import org.xmind.core.marker.IMarkerGroup;
import org.xmind.core.marker.IMarkerResource;
import org.xmind.core.marker.IMarkerResourceAllocator;
import org.xmind.core.marker.IMarkerResourceProvider;
import org.xmind.core.marker.IMarkerSheet;
import org.xmind.core.util.DOMUtils;
import org.xmind.core.util.FileUtils;
import org.xmind.core.util.IPropertiesProvider;
public class MarkerSheetImpl extends MarkerSheet implements
INodeAdaptableFactory, ICoreEventSource, IPropertiesProvider {
private Document implementation;
private IMarkerResourceProvider realResourceProvider;
private ElementRegistry elementRegistry = null;
private NodeAdaptableProvider elementAdaptableProvider = null;
private CoreEventSupport coreEventSupport = null;
private Properties properties = null;
private ManifestImpl manifest = null;
public MarkerSheetImpl(Document implementation,
IMarkerResourceProvider resourceProvider) {
this.implementation = implementation;
this.realResourceProvider = resourceProvider;
init();
}
private void init() {
Element m = DOMUtils.ensureChildElement(implementation,
TAG_MARKER_SHEET);
NS.setNS(NS.Marker, m);
InternalDOMUtils.addVersion(implementation);
}
public Document getImplementation() {
return implementation;
}
public Element getSheetElement() {
return implementation.getDocumentElement();
}
/**
* @param manifest
* the manifest to set
*/
public void setManifest(ManifestImpl manifest) {
this.manifest = manifest;
}
public <T> T getAdapter(Class<T> adapter) {
if (IManifest.class.equals(adapter))
return adapter.cast(manifest);
if (ICoreEventSource.class.equals(adapter))
return adapter.cast(this);
if (IMarkerResourceProvider.class.equals(adapter))
return adapter.cast(realResourceProvider);
if (ICoreEventSupport.class.equals(adapter))
return adapter.cast(getCoreEventSupport());
if (IPropertiesProvider.class.equals(adapter))
return adapter.cast(this);
if (Properties.class.equals(adapter))
return adapter.cast(getProperties());
if (ElementRegistry.class.equals(adapter))
return adapter.cast(getElementRegistry());
if (INodeAdaptableFactory.class.equals(adapter))
return adapter.cast(this);
if (INodeAdaptableProvider.class.equals(adapter))
return adapter.cast(getElementAdapterProvider());
if (adapter.isAssignableFrom(Document.class))
return adapter.cast(implementation);
return super.getAdapter(adapter);
}
protected IMarkerResource getMarkerResource(IMarker marker) {
if (realResourceProvider != null)
return realResourceProvider.getMarkerResource(marker);
return null;
}
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || !(obj instanceof MarkerSheetImpl))
return false;
MarkerSheetImpl that = (MarkerSheetImpl) obj;
return this.implementation == that.implementation;
}
public int hashCode() {
return implementation.hashCode();
}
public String toString() {
return DOMUtils.toString(implementation);
}
public boolean isPermanent() {
return realResourceProvider == null
|| realResourceProvider.isPermanent();
}
public IMarker createMarker(String resourcePath) {
Element markerImpl = implementation.createElement(TAG_MARKER);
DOMUtils.setAttribute(markerImpl, ATTR_RESOURCE, resourcePath);
MarkerImpl marker = new MarkerImpl(markerImpl, this);
getElementRegistry().register(marker);
return marker;
}
public IMarker createMarkerById(String markerId, String resourcePath) {
Element markerImpl = implementation.createElement(TAG_MARKER);
DOMUtils.setAttribute(markerImpl, ATTR_RESOURCE, resourcePath);
MarkerImpl marker = new MarkerImpl(markerImpl, this);
replaceId(marker, markerId);
getElementRegistry().register(marker);
return marker;
}
public IMarkerGroup createMarkerGroupById(String groupId) {
Element groupImpl = implementation.createElement(TAG_MARKER_GROUP);
MarkerGroupImpl group = new MarkerGroupImpl(groupImpl, this);
replaceId(group, groupId);
getElementRegistry().register(group);
return group;
}
public IMarkerGroup createMarkerGroup(boolean singleton) {
Element groupImpl = implementation.createElement(TAG_MARKER_GROUP);
if (singleton)
groupImpl.setAttribute(ATTR_SINGLETON, Boolean.toString(singleton));
MarkerGroupImpl group = new MarkerGroupImpl(groupImpl, this);
getElementRegistry().register(group);
return group;
}
protected IMarker getLocalMarker(String markerId) {
Object element = getElementById(markerId);
if (element != null && element instanceof IMarker)
return (IMarker) element;
return null;
}
protected IMarkerGroup getLocalMarkerGroup(String groupId) {
Object element = getElementById(groupId);
if (element != null && element instanceof IMarkerGroup)
return (IMarkerGroup) element;
return null;
}
public List<IMarkerGroup> getMarkerGroups() {
return DOMUtils.getChildList(getSheetElement(), TAG_MARKER_GROUP,
getElementAdapterProvider());
}
public void addMarkerGroup(IMarkerGroup group) {
Element g = ((MarkerGroupImpl) group).getImplementation();
Element s = getSheetElement();
Node n = s.appendChild(g);
if (n != null) {
int index = DOMUtils.getElementIndex(s, TAG_MARKER_GROUP, g);
if (index >= 0) {
fireIndexedTargetChange(Core.MarkerGroupAdd, group, index);
}
}
}
public void removeMarkerGroup(IMarkerGroup group) {
Element g = ((MarkerGroupImpl) group).getImplementation();
Element s = getSheetElement();
if (g.getParentNode() == s) {
int index = DOMUtils.getElementIndex(s, TAG_MARKER_GROUP, g);
if (index >= 0) {
Node n = s.removeChild(g);
if (n != null) {
fireIndexedTargetChange(Core.MarkerGroupRemove, group,
index);
}
}
}
}
@Override
public boolean isEmpty() {
return !getSheetElement().hasChildNodes();
}
protected Object getElementById(String id) {
Object element = getElementRegistry().getElement(id);
if (element == null) {
Element domElement = implementation.getElementById(id);
if (domElement != null) {
element = getElementAdapter(domElement);
}
}
return element;
}
public ElementRegistry getElementRegistry() {
if (elementRegistry == null)
elementRegistry = new ElementRegistry();
return elementRegistry;
}
protected NodeAdaptableProvider getElementAdapterProvider() {
if (elementAdaptableProvider == null)
elementAdaptableProvider = new NodeAdaptableProvider(
getElementRegistry(), this, implementation);
return elementAdaptableProvider;
}
protected IAdaptable getElementAdapter(Node node) {
return getElementAdapterProvider().getAdaptable(node);
}
public IAdaptable createAdaptable(Node node) {
if (node instanceof Element) {
Element e = (Element) node;
String tagName = e.getTagName();
if (TAG_MARKER_GROUP.equals(tagName)) {
return new MarkerGroupImpl(e, this);
} else if (TAG_MARKER.equals(tagName)) {
return new MarkerImpl(e, this);
}
}
return null;
}
@Deprecated
public void save(OutputStream out) throws IOException, CoreException {
DOMUtils.save(implementation, out, false);
}
private static final String OLD_MARKERLISTS_XML = "markerlists.xml"; //$NON-NLS-1$
@SuppressWarnings("unused")
private static final String OLD_TAG_MARKER_LISTS = "markerLists"; //$NON-NLS-1$
private static final String OLD_TAG_MARKER_LIST = "markerList"; //$NON-NLS-1$
private static final String OLD_TAG_MARKER = "marker"; //$NON-NLS-1$
private static final String OLD_ATT_NAME = "name"; //$NON-NLS-1$
private static final String OLD_ATT_ID = "id"; //$NON-NLS-1$
private static final String OLD_ATT_FILE = "file"; //$NON-NLS-1$
@Deprecated
public void importFrom(String sourcePath)
throws IOException, CoreException {
File sourceFile = new File(sourcePath);
IInputSource source;
if (sourceFile.isDirectory()) {
source = new DirectoryInputSource(sourceFile);
} else {
source = new ZipFileInputSource(new ZipFile(sourceFile));
}
try {
importFrom(source, new File(sourcePath).getName());
} finally {
if (source instanceof ZipFileInputSource) {
((ZipFileInputSource) source).closeZipFile();
}
}
}
@Deprecated
public void importFrom(IInputSource source)
throws IOException, CoreException {
importFrom(source, null);
}
/*
* (non-Javadoc)
* @see org.xmind.core.marker.IMarkerSheet#importFrom(org.xmind.core.io.
* IInputSource , java.lang.String)
*/
@Deprecated
public void importFrom(IInputSource source, String groupName)
throws IOException, CoreException {
if (source.hasEntry(ArchiveConstants.MARKER_SHEET_XML)) {
InputStream is = source
.openEntryStream(ArchiveConstants.MARKER_SHEET_XML);
IMarkerSheet sourceMarkerSheet = Core.getMarkerSheetBuilder()
.loadFromStream(is,
new MarkerResourceProvider(source, null));
importFrom(sourceMarkerSheet);
} else if (source.hasEntry(OLD_MARKERLISTS_XML)) {
importFromOldMarkerSheet(source);
} else {
importAllAsNewGroup(source, groupName);
}
}
@Deprecated
private void importAllAsNewGroup(IInputSource source, String groupName)
throws IOException {
IMarkerGroup group = createMarkerGroup(false);
if (groupName == null) {
groupName = createGroupName();
}
group.setName(groupName);
Iterator<String> entries = source.getEntries();
while (entries.hasNext()) {
String entry = entries.next();
InputStream is = source.openEntryStream(entry);
if (is != null) {
IMarker marker = createMarker(entry);
marker.setName(FileUtils.getFileName(entry));
group.addMarker(marker);
IMarkerResource resource = marker.getResource();
if (resource != null) {
OutputStream os = resource.openOutputStream();
if (os != null) {
try {
FileUtils.transfer(is, os, true);
} catch (IOException ignore) {
}
}
}
}
}
addMarkerGroup(group);
}
private String createGroupName() {
return "Group " + (getMarkerGroups().size() + 1); //$NON-NLS-1$
//return Core.getIdFactory().createId();
}
@Deprecated
private void importFromOldMarkerSheet(IInputSource source)
throws IOException {
InputStream is = source.openEntryStream(OLD_MARKERLISTS_XML);
if (is == null)
throw new FileNotFoundException();
Document document = DOMUtils.loadDocument(is);
Iterator<Element> listIt = DOMUtils.childElementIterByTag(
document.getDocumentElement(), OLD_TAG_MARKER_LIST);
while (listIt.hasNext()) {
Element listEle = listIt.next();
String listId = listEle.getAttribute(OLD_ATT_ID);
IMarkerGroup existingGroup = findMarkerGroup(listId);
if (existingGroup != null) {
importGroupFromOld(source, listEle, existingGroup);
} else {
IMarkerGroup newGroup = createMarkerGroup(false);
replaceId(newGroup, listId);
addMarkerGroup(newGroup);
importGroupFromOld(source, listEle, newGroup);
}
}
}
private void replaceId(IAdaptable adaptable, String newId) {
Element ele = (Element) adaptable.getAdapter(Element.class);
if (ele == null)
return;
String oldId = ele.getAttribute(DOMConstants.ATTR_ID);
getElementRegistry().unregisterByKey(oldId);
ele.setAttribute(DOMConstants.ATTR_ID, newId);
getElementRegistry().registerByKey(newId, adaptable);
}
@Deprecated
private void importGroupFromOld(IInputSource source, Element listEle,
IMarkerGroup targetGroup) throws IOException {
targetGroup.setName(listEle.getAttribute(OLD_ATT_NAME));
Iterator<Element> markerIt = DOMUtils.childElementIterByTag(listEle,
OLD_TAG_MARKER);
while (markerIt.hasNext()) {
Element markerEle = markerIt.next();
String file = DOMUtils.getAttribute(markerEle, OLD_ATT_FILE);
if (file != null) {
String markerId = markerEle.getAttribute(OLD_ATT_ID);
IMarker targetMarker = findMarker(markerId);
if (targetMarker == null) {
targetMarker = targetGroup.getOwnedSheet().createMarker(
createGroupName() + FileUtils.getExtension(file));
replaceId(targetMarker, markerId);
targetGroup.addMarker(targetMarker);
}
targetMarker.setName(markerEle.getAttribute(OLD_ATT_NAME));
String oldEntryName = listEle.getAttribute(OLD_ATT_ID) + "/" //$NON-NLS-1$
+ file;
IMarkerResource newRes = targetMarker.getResource();
if (newRes != null) {
OutputStream os = newRes.openOutputStream();
if (os != null) {
if (source.hasEntry(oldEntryName)) {
InputStream mis = source
.openEntryStream(oldEntryName);
FileUtils.transfer(mis, os, true);
}
}
}
}
}
}
@Deprecated
public void importFrom(IMarkerSheet sheet) {
try {
importFromChecked(sheet);
} catch (Exception e) {
Core.getLogger().log(e);
}
}
@Deprecated
public void importFromChecked(IMarkerSheet sheet)
throws IOException, CoreException {
for (IMarkerGroup group : sheet.getMarkerGroups()) {
importGroupChecked(group);
}
}
@Deprecated
public IMarkerGroup importGroup(IMarkerGroup group) {
try {
return importGroupChecked(group);
} catch (Exception e) {
Core.getLogger().log(e);
return null;
}
}
@Deprecated
public IMarkerGroup importGroupChecked(IMarkerGroup group)
throws IOException, CoreException {
String id = group.getId();
IMarkerGroup existingGroup = findMarkerGroup(id);
if (existingGroup != null) {
existingGroup.setName(group.getName());
importGroup(group, existingGroup);
if (existingGroup.getParent() == null)
addMarkerGroup(existingGroup);
return existingGroup;
}
IMarkerGroup targetGroup = createMarkerGroup(group.isSingleton());
replaceId(targetGroup, group.getId());
targetGroup.setName(group.getName());
importGroup(group, targetGroup);
addMarkerGroup(targetGroup);
return targetGroup;
}
@Deprecated
private void importGroup(IMarkerGroup sourceGroup, IMarkerGroup targetGroup)
throws IOException {
for (IMarker sourceMarker : sourceGroup.getMarkers()) {
String id = sourceMarker.getId();
IMarker targetMarker = getLocalMarker(id);
if (targetMarker == null
|| !targetGroup.equals(targetMarker.getParent())) {
targetMarker = targetGroup.getOwnedSheet()
.createMarker(sourceMarker.getResourcePath());
replaceId(targetMarker, sourceMarker.getId());
targetGroup.addMarker(targetMarker);
}
targetMarker.setName(sourceMarker.getName());
IMarkerResource sourceRes = sourceMarker.getResource();
if (sourceRes != null) {
InputStream is = sourceRes.openInputStream();
IMarkerResource targetRes = targetMarker.getResource();
if (targetRes != null) {
OutputStream os = targetRes.openOutputStream();
FileUtils.transfer(is, os, true);
}
}
}
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public ICoreEventRegistration registerCoreEventListener(String type,
ICoreEventListener listener) {
return getCoreEventSupport().registerCoreEventListener(this, type,
listener);
}
public CoreEventSupport getCoreEventSupport() {
if (coreEventSupport != null)
return coreEventSupport;
coreEventSupport = new CoreEventSupport();
return coreEventSupport;
}
private void fireIndexedTargetChange(String type, Object target,
int index) {
getCoreEventSupport().dispatchIndexedTargetChange(this, type, target,
index);
}
/*
* (non-Javadoc)
* @see org.xmind.core.marker.IMarkerSheet#createMarkerResourcePath(java.io.
* InputStream, java.lang.String)
*/
public String allocateMarkerResource(InputStream source,
String suggestedPath) throws IOException {
if (realResourceProvider != null
&& realResourceProvider instanceof IMarkerResourceAllocator) {
return ((IMarkerResourceAllocator) realResourceProvider)
.allocateMarkerResource(source, suggestedPath);
}
return null;
}
}