All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xmind.core.internal.dom.WorkbookSaver Maven / Gradle / Ivy

Go to download

Plugin that manages JBehave stories storage in XMind mindmap file, which allows design jBehave tests right after the brainstorm

The newest version!
/* ******************************************************************************
 * 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_ID;
import static org.xmind.core.internal.dom.DOMConstants.TAG_SHEET;
import static org.xmind.core.internal.zip.ArchiveConstants.CONTENT_XML;
import static org.xmind.core.internal.zip.ArchiveConstants.MANIFEST_XML;
import static org.xmind.core.internal.zip.ArchiveConstants.META_XML;
import static org.xmind.core.internal.zip.ArchiveConstants.PATH_MARKER_SHEET;
import static org.xmind.core.internal.zip.ArchiveConstants.PATH_REVISIONS;
import static org.xmind.core.internal.zip.ArchiveConstants.REVISIONS_XML;
import static org.xmind.core.internal.zip.ArchiveConstants.STYLES_XML;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.zip.ZipOutputStream;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

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.IChecksumStream;
import org.xmind.core.IEncryptionData;
import org.xmind.core.IFileEntry;
import org.xmind.core.IManifest;
import org.xmind.core.IRevision;
import org.xmind.core.IRevisionManager;
import org.xmind.core.internal.InternalCore;
import org.xmind.core.internal.security.Crypto;
import org.xmind.core.internal.zip.ArchiveConstants;
import org.xmind.core.internal.zip.ZipStreamOutputTarget;
import org.xmind.core.io.DirectoryOutputTarget;
import org.xmind.core.io.ICloseableOutputTarget;
import org.xmind.core.io.IInputSource;
import org.xmind.core.io.IOutputTarget;
import org.xmind.core.marker.IMarkerSheet;
import org.xmind.core.style.IStyleSheet;
import org.xmind.core.util.DOMUtils;

/**
 * @author Frank Shaka
 * 
 */
public class WorkbookSaver {

    private static class WorkbookSaveSession {

        /**
         * The workbook to save.
         */
        private final WorkbookImpl workbook;

        /**
         * The multi-entry output target to save to.
         */
        private final IOutputTarget target;

        /**
         * Entry paths that have been saved.
         */
        private Set savedEntries = new HashSet();

        /**
         * DOM transformer factory, lazy created.
         */
        private TransformerFactory transformerFactory = null;

        /**
         * Constructs a new WorkbookSaveSession.
         * 
         * @param workbook
         * @param target
         */
        public WorkbookSaveSession(WorkbookImpl workbook, IOutputTarget target) {
            this.workbook = workbook;
            this.target = target;
        }

        /**
         * The main saving process.
         * 
         * @throws java.io.IOException
         * @throws CoreException
         */
        public synchronized void save() throws IOException, CoreException {
            try {
                try {
                    saveMeta();
                    saveContent();
                    saveMarkerSheet();
                    saveStyleSheet();
                    if (!workbook.isSkipRevisionsWhenSaving()) {
                        saveRevisions();
                    }
                    copyOtherStaff();
                    saveManifest();
                } finally {
                    clearEncryptionData();
                }
            } finally {
                if (target instanceof ICloseableOutputTarget) {
                    ((ICloseableOutputTarget) target).close();
                }
            }
        }

        private void saveManifest() throws IOException, CoreException {
            saveDOM(workbook.getManifest(), target, MANIFEST_XML);
        }

        private void saveStyleSheet() throws IOException, CoreException {
            IStyleSheet styleSheet = workbook.getStyleSheet();
            if (!styleSheet.isEmpty()) {
                saveDOM(styleSheet, target, STYLES_XML);
            }
        }

        private void saveMarkerSheet() throws IOException, CoreException {
            IMarkerSheet markerSheet = workbook.getMarkerSheet();
            if (!markerSheet.isEmpty()) {
                saveDOM(markerSheet, target, PATH_MARKER_SHEET);
            }
        }

        private void saveRevisions() throws IOException, CoreException {
            Iterator sheets = DOMUtils.childElementIterByTag(
                    workbook.getWorkbookElement(), TAG_SHEET);
            while (sheets.hasNext()) {
                Element sheetEle = sheets.next();
                String sheetId = sheetEle.getAttribute(ATTR_ID);
                IRevisionManager manager = workbook.getRevisionRepository()
                        .getRevisionManager(sheetId, IRevision.SHEET);
                String path = PATH_REVISIONS + sheetId + "/" + REVISIONS_XML; //$NON-NLS-1$
                saveDOM(manager, target, path);
            }
        }

        private void saveContent() throws IOException, CoreException {
            saveDOM(workbook, target, CONTENT_XML);
        }

        private void saveMeta() throws IOException, CoreException {
            saveDOM(workbook.getMeta(), target, META_XML);
        }

        private void copyOtherStaff() throws IOException, CoreException {
            IInputSource source = workbook.getTempStorage().getInputSource();
            IManifest manifest = workbook.getManifest();
            for (IFileEntry entry : manifest.getFileEntries()) {
                if (!entry.isDirectory()) {
                    String entryPath = entry.getPath();
                    if (shouldSaveEntry(entryPath)) {
                        copyEntry(source, target, entryPath);
                        markSaved(entryPath);
                    }
                }
            }
        }

        private synchronized void copyEntry(IInputSource source,
                IOutputTarget target, String entryPath) throws IOException,
                CoreException {
            InputStream in = getInputStream(source, entryPath);
            if (in == null) {
                Core.getLogger().log(
                        "Save workbook: failed to copy entry, input stream not avaiable: " //$NON-NLS-1$
                                + entryPath);
                return; // Entry source not found.
            }

            try {
                long time = source.getEntryTime(entryPath);
                if (time >= 0) {
                    target.setEntryTime(entryPath, time);
                }
                OutputStream out = getOutputStream(target, entryPath);
                try {
                    int numBytes;
                    byte[] byteBuffer = new byte[4096];
                    while ((numBytes = in.read(byteBuffer)) > 0) {
                        out.write(byteBuffer, 0, numBytes);
                    }
                    recordChecksum(entryPath, out);
                } finally {
                    out.close();
                }
            } finally {
                in.close();
            }
        }

        private boolean shouldSaveEntry(String entryPath) {
            return entryPath != null
                    && !"".equals(entryPath) //$NON-NLS-1$
                    && !MANIFEST_XML.equals(entryPath)
                    && !hasBeenSaved(entryPath)
                    && !(workbook.isSkipRevisionsWhenSaving() && entryPath
                            .startsWith(ArchiveConstants.PATH_REVISIONS));
        }

        private void clearEncryptionData() {
            for (IFileEntry entry : workbook.getManifest().getFileEntries()) {
                entry.deleteEncryptionData();
            }
        }

        private void saveDOM(IAdaptable domAdapter, IOutputTarget target,
                String entryPath) throws IOException, CoreException {
            Node node = (Node) domAdapter.getAdapter(Node.class);
            if (node == null) {
                Core.getLogger().log(
                        "SaveWorkbook: No DOM node available for entry: " //$NON-NLS-1$
                                + entryPath);
                return;
            }

            if (transformerFactory == null) {
                try {
                    transformerFactory = TransformerFactory.newInstance();
                } catch (TransformerFactoryConfigurationError error) {
                    throw new CoreException(
                            Core.ERROR_FAIL_ACCESS_XML_TRANSFORMER,
                            "Failed to obtain XML transformer factory.", error); //$NON-NLS-1$
                }
            }

            Transformer transformer;
            try {
                transformer = transformerFactory.newTransformer();
            } catch (TransformerConfigurationException error) {
                throw new CoreException(Core.ERROR_FAIL_ACCESS_XML_TRANSFORMER,
                        "Failed to create XML transformer for DOM entry '" //$NON-NLS-1$
                                + entryPath + "'.", error); //$NON-NLS-1$
            }

            OutputStream out = getOutputStream(target, entryPath);
            try {
                transformer.transform(new DOMSource(node),
                        new StreamResult(out));
            } catch (TransformerException e) {
                throw new IOException(e.getLocalizedMessage(), e);
            } finally {
                out.close();
            }
            recordChecksum(entryPath, out);
            markSaved(entryPath);
        }

        private void recordChecksum(String entryPath, Object checksumProvider)
                throws IOException {
            if (checksumProvider instanceof IChecksumStream) {
                IEncryptionData encData = workbook.getManifest()
                        .getEncryptionData(entryPath);
                if (encData != null && encData.getChecksumType() != null) {
                    String checksum = ((IChecksumStream) checksumProvider)
                            .getChecksum();
                    if (checksum != null) {
                        encData.setAttribute(checksum,
                                DOMConstants.ATTR_CHECKSUM);
                    }
                }
            }
        }

        private InputStream getInputStream(IInputSource source, String entryPath) {
            if (source.hasEntry(entryPath)) {
                return source.getEntryStream(entryPath);
            }
            return null;
        }

        private OutputStream getOutputStream(IOutputTarget target,
                String entryPath) throws IOException, CoreException {
            OutputStream out = target.openEntryStream(entryPath);

            String password = workbook.getPassword();
            if (password == null)
                return out;

            IFileEntry entry = workbook.getManifest().getFileEntry(entryPath);
            if (entry == null)
                return out;

            if (ignoresEncryption(entry, entryPath))
                return out;

            IEncryptionData encData = entry.createEncryptionData();
            return Crypto.creatOutputStream(out, true, encData, password);
        }

        private boolean ignoresEncryption(IFileEntry entry, String entryPath) {
            return MANIFEST_XML.equals(entryPath)
                    || ((FileEntryImpl) entry).isIgnoreEncryption();
        }

        private boolean hasBeenSaved(String entryPath) {
            return savedEntries.contains(entryPath);
        }

        private void markSaved(String entryPath) {
            savedEntries.add(entryPath);
        }

    }

    /**
     * The workbook to save.
     */
    private final WorkbookImpl workbook;

    /**
     * The last target saved to.
     */
    private IOutputTarget lastTarget;

    /**
     * (Optional) The absolute path representing a ZIP file target
     */
    private String file;

    /**
     * Whether to skip revisions when saving.
     */
    private boolean skipRevisions = false;

    /**
     * @param workbook
     * @param file
     */
    public WorkbookSaver(WorkbookImpl workbook, String file) {
        super();
        this.workbook = workbook;
        this.file = file;
    }

    /**
     * @return the file path
     */
    public String getFile() {
        return file;
    }

    public void setFile(String file) {
        this.file = file;
    }

    /**
     * @param skipRevisions
     *            the skipRevisions to set
     */
    public void setSkipRevisions(boolean skipRevisions) {
        this.skipRevisions = skipRevisions;
    }

    public boolean isSkipRevisions() {
        return skipRevisions;
    }

    /**
     * 
     * @throws java.io.IOException
     * @throws CoreException
     */
    public synchronized void save() throws IOException, CoreException {
        doSave(this.lastTarget);
    }

    /**
     * 
     * @param output
     * @throws java.io.IOException
     * @throws CoreException
     */
    public synchronized void save(OutputStream output) throws IOException,
            CoreException {
        doSave(new ZipStreamOutputTarget(new ZipOutputStream(output)));
    }

    public synchronized void save(String file) throws IOException,
            CoreException {
        if (new File(file).isDirectory()) {
            doSave(new DirectoryOutputTarget(file));
        } else {
            FileOutputStream fout = new FileOutputStream(file);
            try {
                ZipOutputStream stream = new ZipOutputStream(fout);
                try {
                    doSave(new ZipStreamOutputTarget(stream));
                } finally {
                    stream.close();
                }
            } finally {
                fout.close();
            }
        }
        this.file = file;
    }

    public synchronized void save(IOutputTarget target) throws IOException,
            CoreException {
        doSave(target);
        this.lastTarget = target;
    }

    /**
     * 
     * @param target
     * @throws java.io.IOException
     * @throws CoreException
     */
    private synchronized void doSave(IOutputTarget target) throws IOException,
            CoreException {
        if (target == null)
            throw new FileNotFoundException("No target to save."); //$NON-NLS-1$

        if (InternalCore.DEBUG_WORKBOOK_SAVE)
            Core.getLogger().log(
                    "WorkbookSaver: About to save workbook to output target " //$NON-NLS-1$
                            + target.toString());
        new WorkbookSaveSession(workbook, target).save();
        if (InternalCore.DEBUG_WORKBOOK_SAVE)
            Core.getLogger().log(
                    "WorkbookSaver: Finished saving workbook to output target " //$NON-NLS-1$
                            + target.toString());
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy