/* ******************************************************************************
* 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_FULL_PATH;
import static org.xmind.core.internal.dom.DOMConstants.ATTR_MEDIA_TYPE;
import static org.xmind.core.internal.dom.DOMConstants.TAG_FILE_ENTRY;
import static org.xmind.core.internal.dom.DOMConstants.TAG_MANIFEST;
import static org.xmind.core.internal.dom.InternalDOMUtils.getParentPath;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xmind.core.Core;
import org.xmind.core.IEncryptionData;
import org.xmind.core.IFileEntry;
import org.xmind.core.IFileEntryFilter;
import org.xmind.core.IWorkbook;
import org.xmind.core.internal.Manifest;
import org.xmind.core.util.DOMUtils;
import org.xmind.core.util.FileUtils;
public class ManifestImpl extends Manifest {
private static final Collection<IFileEntry> NO_ENTRIES = Collections
.emptyList();
private static final Comparator<String> ENTRY_COMPARATOR = new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2);
}
};
private Document implementation;
private WorkbookImpl ownedWorkbook;
private Map<String, IFileEntry> entries = null;
public ManifestImpl(Document implementation) {
this.implementation = implementation;
init();
}
/**
*
* @param workbook
*/
protected void setWorkbook(WorkbookImpl workbook) {
this.ownedWorkbook = workbook;
}
private void init() {
Element m = DOMUtils.ensureChildElement(implementation, TAG_MANIFEST);
NS.setNS(NS.Manifest, m);
}
public Document getImplementation() {
return implementation;
}
public Element getManifestElement() {
return implementation.getDocumentElement();
}
public Object getAdapter(Class adapter) {
if (adapter == Document.class || adapter == Node.class)
return implementation;
if (adapter == IWorkbook.class)
return ownedWorkbook;
return super.getAdapter(adapter);
}
public IWorkbook getOwnedWorkbook() {
return ownedWorkbook;
}
/*
* (non-Javadoc)
*
* @see org.xmind.core.IWorkbookComponent#isOrphan()
*/
public boolean isOrphan() {
return ownedWorkbook != null;
}
protected Collection<IFileEntry> getAllRegisteredEntries() {
if (entries == null)
return NO_ENTRIES;
return entries.values();
}
/*
* (non-Javadoc)
*
* @see org.xmind.core.IManifest#iterFileEntries()
*/
public Iterator<IFileEntry> iterFileEntries() {
return iterFileEntries(null);
}
/*
* (non-Javadoc)
*
* @see org.xmind.core.IManifest#iterFileEntries(org.xmind.core.IManifest.
* IFileEntryFilter)
*/
public Iterator<IFileEntry> iterFileEntries(final IFileEntryFilter filter) {
final Iterator<Element> it = DOMUtils.childElementIterByTag(
getManifestElement(), TAG_FILE_ENTRY);
return new Iterator<IFileEntry>() {
IFileEntry next = findNext();
private IFileEntry findNext() {
while (it.hasNext()) {
Element e = it.next();
if (e.hasAttribute(ATTR_FULL_PATH) && select(e)) {
return getFileEntry(e);
}
}
return null;
}
private boolean select(Element e) {
if (filter == null)
return true;
String path = e.getAttribute(ATTR_FULL_PATH);
String mediaType = e.getAttribute(ATTR_MEDIA_TYPE);
boolean isDirectory = path.endsWith("/"); //$NON-NLS-1$
return filter.select(path, mediaType, isDirectory);
}
public void remove() {
}
public IFileEntry next() {
IFileEntry n = next;
next = findNext();
return n;
}
public boolean hasNext() {
return next != null;
}
};
}
public IFileEntry getFileEntry(String path) {
if (path == null)
return null;
IFileEntry entry = findEntry(path);
if (entry == null) {
while (path.startsWith("/")) { //$NON-NLS-1$
path = path.substring(1, path.length());
entry = findEntry(path);
if (entry != null)
break;
}
}
if (entry == null) {
entry = findEntry("/" + path); //$NON-NLS-1$
}
return entry;
}
private IFileEntry findEntry(String path) {
IFileEntry entry = null;
if (entries != null)
entry = entries.get(path);
if (entry == null) {
Element e = findEntryElementByPath(path);
if (e != null)
entry = createFileEntry(path, e);
}
return entry;
}
private Element findEntryElementByPath(String path) {
Iterator<Element> it = DOMUtils.childElementIterByTag(
getManifestElement(), TAG_FILE_ENTRY);
while (it.hasNext()) {
Element e = it.next();
if (path.equals(e.getAttribute(ATTR_FULL_PATH)))
return e;
}
return null;
}
public IFileEntry createFileEntry(String path) {
return createFileEntry(path, ""); //$NON-NLS-1$
}
public IFileEntry createFileEntry(String path, String mediaType) {
IFileEntry entry = getFileEntry(path);
if (entry != null)
return entry;
String parent = InternalDOMUtils.getParentPath(path);
if (parent != null) {
createFileEntry(parent);
}
Element e = implementation.createElement(TAG_FILE_ENTRY);
e.setAttribute(ATTR_FULL_PATH, path);
e.setAttribute(ATTR_MEDIA_TYPE, mediaType);
return createFileEntry(path, e);
}
private IFileEntry createFileEntry(String path, Element entryElement) {
IFileEntry entry = new FileEntryImpl(entryElement, this);
if (entries == null)
entries = new TreeMap<String, IFileEntry>(ENTRY_COMPARATOR);
entries.put(path, entry);
return entry;
}
private IFileEntry getFileEntry(Element element) {
String path = element.getAttribute(ATTR_FULL_PATH);
if (entries != null) {
IFileEntry entry = entries.get(path);
if (entry != null)
return entry;
}
return createFileEntry(path, element);
}
// public void insertElement(IFileEntry entry) {
// Element e = (Element) entry.getAdapter(Element.class);
//
// Element ed = DOMUtils.ensureChildElement(e, "encryption-data");
// ed.setAttribute("data1", "123");
//
// }
protected void insertFileEntry(IFileEntry entry) {
Element e = (Element) entry.getAdapter(Element.class);
if (e != null) {
insertFileEntryImpl(e);
}
}
protected void removeFileEntry(IFileEntry entry) {
Element e = (Element) entry.getAdapter(Element.class);
if (e != null) {
Element m = getManifestElement();
if (m == e.getParentNode())
m.removeChild(e);
}
}
private void insertFileEntryImpl(Element entryElement) {
Element e = findInsertLocation(entryElement);
if (e != null) {
getManifestElement().insertBefore(entryElement, e);
} else {
getManifestElement().appendChild(entryElement);
}
}
private Element findInsertLocation(Element entryElement) {
if (entryElement.hasAttribute(ATTR_FULL_PATH))
return findInsertLocation(entryElement,
entryElement.getAttribute(ATTR_FULL_PATH));
return null;
}
private Element findInsertLocation(Element entryElement, String path) {
Iterator<Element> it = DOMUtils.childElementIterByTag(
getManifestElement(), TAG_FILE_ENTRY);
while (it.hasNext()) {
Element e = it.next();
if (e != entryElement && e.hasAttribute(ATTR_FULL_PATH)) {
String p = e.getAttribute(ATTR_FULL_PATH);
if (p != null && path.compareToIgnoreCase(p) < 0) {
return e;
}
}
}
return null;
}
public IFileEntry createAttachmentFromFilePath(String sourcePath)
throws IOException {
return createAttachmentFromFilePath(sourcePath, null);
}
public IFileEntry createAttachmentFromFilePath(String sourcePath,
String mediaType) throws IOException {
if (sourcePath == null)
throw new IllegalArgumentException("Path is null!"); //$NON-NLS-1$
File file = new File(sourcePath);
if (!file.exists())
throw new FileNotFoundException("Source path does not exists."); //$NON-NLS-1$
if (file.isFile()) {
IFileEntry entry = createAttachmentFromStream(new FileInputStream(
sourcePath), sourcePath, mediaType);
if (entry != null) {
entry.setTime(file.lastModified());
}
return entry;
}
if (file.isDirectory()) {
String fileName = file.getName();
String path = makeAttachmentPath(fileName, true);
if (mediaType == null)
mediaType = FileUtils.getMediaType(fileName);
IFileEntry root = createFileEntry(path, mediaType);
if (root != null) {
importDirectory(path, file);
}
return root;
}
throw new IllegalArgumentException(
"Unknown file type (neither a file nor a directory)"); //$NON-NLS-1$
}
protected void importDirectory(String parentPath, File dir)
throws IOException {
for (String sub : dir.list()) {
File f = new File(dir, sub);
if (f.isFile()) {
String path = parentPath == null ? sub : parentPath + sub;
String mediaType = FileUtils.getMediaType(sub);
IFileEntry e = createFileEntry(path, mediaType);
if (e != null) {
OutputStream os = e.getOutputStream();
if (os != null) {
FileUtils.transfer(new FileInputStream(f), os);
}
e.setTime(f.lastModified());
}
} else if (f.isDirectory()) {
String path = parentPath == null ? sub + "/" : //$NON-NLS-1$
parentPath + sub + "/"; //$NON-NLS-1$
importDirectory(path, f);
}
}
}
public IFileEntry createAttachmentFromStream(InputStream stream,
String sourceName) throws IOException {
return createAttachmentFromStream(stream, sourceName, null);
}
public IFileEntry createAttachmentFromStream(InputStream stream,
String sourceName, String mediaType) throws IOException {
if (sourceName == null || stream == null)
return null;
String path = makeAttachmentPath(sourceName, false);
if (mediaType == null)
mediaType = FileUtils.getMediaType(sourceName);
IFileEntry entry = createFileEntry(path, mediaType);
if (entry != null) {
OutputStream os = entry.getOutputStream();
if (os == null)
throw new IOException();
FileUtils.transfer(stream, os);
}
return entry;
}
public IFileEntry cloneEntry(IFileEntry sourceEntry, String targetPath)
throws IOException {
if (sourceEntry == null || targetPath == null)
return null;
IFileEntry existingEntry = getFileEntry(targetPath);
if (existingEntry != null) {
return null;
}
if (sourceEntry.isDirectory()) {
if (!targetPath.endsWith("/")) //$NON-NLS-1$
targetPath = targetPath + "/"; //$NON-NLS-1$
importDirectoryEntry(targetPath, sourceEntry);
} else {
importFileEntry(targetPath, sourceEntry);
}
return getFileEntry(targetPath);
}
private void importFileEntry(String path, IFileEntry sourceEntry)
throws IOException {
InputStream in = sourceEntry.getInputStream();
if (in != null) {
IFileEntry entry = createFileEntry(path, sourceEntry.getMediaType());
if (entry != null) {
OutputStream os = entry.getOutputStream();
if (os != null) {
FileUtils.transfer(in, os);
}
}
entry.setTime(sourceEntry.getTime());
}
}
private void importDirectoryEntry(String parentPath, IFileEntry sourceEntry)
throws IOException {
String sourceParentPath = getParentPath(sourceEntry.getPath());
for (IFileEntry sourceSubEntry : sourceEntry.getSubEntries()) {
String sourceSubPath = sourceSubEntry.getPath();
if (sourceSubPath != null) {
String subPath = sourceSubPath.substring(sourceParentPath
.length());
if (parentPath != null) {
subPath = parentPath + subPath;
}
if (!sourceSubEntry.isDirectory()) {
importFileEntry(subPath, sourceSubEntry);
}
}
}
if (getFileEntry(parentPath) == null) {
createFileEntry(parentPath, sourceEntry.getMediaType());
}
}
public IFileEntry cloneEntryAsAttachment(IFileEntry sourceEntry)
throws IOException {
String sourcePath = sourceEntry.getPath();
String path = makeAttachmentPath(sourcePath, sourceEntry.isDirectory());
IFileEntry cloneEntry = cloneEntry(sourceEntry, path);
return cloneEntry;
}
public String makeAttachmentPath(String source) {
return makeAttachmentPath(source, false);
}
public String makeAttachmentPath(String source, boolean directory) {
String path = "attachments/" + Core.getIdFactory().createId() //$NON-NLS-1$
+ FileUtils.getExtension(source);
if (directory)
path += "/"; //$NON-NLS-1$
return path;
}
/*
* (non-Javadoc)
*
* @see org.xmind.core.IManifest#getEncryptionData(java.lang.String)
*/
public IEncryptionData getEncryptionData(String entryPath) {
IFileEntry entry = getFileEntry(entryPath);
if (entry != null)
return entry.getEncryptionData();
return null;
}
}