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

org.xmind.core.internal.dom.WorkbookLoader 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.zip.ArchiveConstants.MANIFEST_XML;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;

import org.w3c.dom.Document;
import org.xmind.core.Core;
import org.xmind.core.CoreException;
import org.xmind.core.IChecksumStream;
import org.xmind.core.IEncryptionData;
import org.xmind.core.IEncryptionHandler;
import org.xmind.core.IFileEntry;
import org.xmind.core.IManifest;
import org.xmind.core.IRevision;
import org.xmind.core.ISheet;
import org.xmind.core.IWorkbook;
import org.xmind.core.internal.compatibility.Compatibility;
import org.xmind.core.internal.security.Crypto;
import org.xmind.core.internal.zip.ArchiveConstants;
import org.xmind.core.io.IInputSource;
import org.xmind.core.io.IOutputTarget;
import org.xmind.core.io.IStorage;
import org.xmind.core.marker.IMarkerSheet;
import org.xmind.core.style.IStyleSheet;

/**
 * @author frankshaka
 * 
 */
public class WorkbookLoader extends XMLLoader {

    /**
     * 
     */
    private WorkbookBuilderImpl builder;

    /**
     * 
     */
    private IInputSource source;

    /**
     * 
     */
    private IStorage storage;

    /**
     * 
     */
    private IEncryptionHandler encryptionHandler;

    /**
     * 
     */
    private WorkbookImpl workbook = null;

    /**
     * 
     */
    private Set loadedEntries = new HashSet();

    /**
     * 
     */
    private ManifestImpl manifest = null;

    /**
     * 
     */
    private boolean ignoreCopy;

    /**
     * 
     */
    private String password = null;

    /**
     * Byte buffer for transfering entries.
     */
    private byte[] byteBuffer = new byte[4096];

    /**
     * 
     * @param builder
     * @param source
     * @param storage
     * @param encryptionHandler
     */
    public WorkbookLoader(WorkbookBuilderImpl builder, IInputSource source,
            IStorage storage, IEncryptionHandler encryptionHandler)
            throws CoreException {
        super();
        this.builder = builder;
        if (source == null && storage != null) {
            source = storage.getInputSource();
            this.ignoreCopy = true;
        } else {
            this.ignoreCopy = false;
        }
        this.source = source;
        this.storage = storage;
        this.encryptionHandler = encryptionHandler;

    }

    public IWorkbook load() throws IOException, CoreException {
        password = null;
        manifest = null;
        try {
            doLoad();
        } finally {
            manifest = null;
            password = null;
        }
        return workbook;
    }

    /**
     * The main loading process.
     * 
     * @throws java.io.IOException
     * @throws CoreException
     */
    private void doLoad() throws IOException, CoreException {
        loadManifest();

        if (loadOldFormat())
            return;

        loadContents();
        loadMeta();
        loadStyleSheet();
        loadMarkerSheet();

        copyOtherStaff();

        initWorkbook();
        clearEncryptionData();
    }

    private void loadManifest() throws IOException, CoreException {
        Document doc = forceLoadXML(MANIFEST_XML);
        this.manifest = new ManifestImpl(doc);
    }

    private boolean loadOldFormat() throws IOException, CoreException {
        IWorkbook compatible = Compatibility.loadCompatibleWorkbook(source,
                this, storage);
        if (compatible != null) {
            workbook = (WorkbookImpl) compatible;
            return true;
        }
        return false;
    }

    private void loadContents() throws IOException, CoreException {
        Document doc = loadXMLFile(source, ArchiveConstants.CONTENT_XML);
        workbook = new WorkbookImpl(doc);
        workbook.setManifest(manifest);
    }

    private void loadMeta() throws IOException, CoreException {
        Document doc = forceLoadXML(ArchiveConstants.META_XML);
        workbook.setMeta(new MetaImpl(doc));
    }

    private void loadStyleSheet() throws IOException, CoreException {
        try {
            IStyleSheet styleSheet = ((StyleSheetBuilderImpl) Core
                    .getStyleSheetBuilder()).loadFromInputSource(source, this);
            ((StyleSheetImpl) styleSheet).setManifest(manifest);
            workbook.setStyleSheet((StyleSheetImpl) styleSheet);
        } catch (IOException e) {
            throw e;
        } catch (CoreException e) {
            if (e.getType() != Core.ERROR_NO_SUCH_ENTRY)
                throw e;
        }
    }

    private void loadMarkerSheet() throws IOException, CoreException {
        try {
            IMarkerSheet markerSheet = ((MarkerSheetBuilderImpl) Core
                    .getMarkerSheetBuilder()).loadFromInputSource(source, this,
                    new WorkbookMarkerResourceProvider(workbook));
            workbook.setMarkerSheet((MarkerSheetImpl) markerSheet);
        } catch (IOException e) {
            throw e;
        } catch (CoreException e) {
            if (e.getType() != Core.ERROR_NO_SUCH_ENTRY)
                throw e;
        }
    }

    private void copyOtherStaff() throws IOException, CoreException {
        if (ignoreCopy || source == null || storage == null)
            return;

        if (!source.equals(storage.getInputSource())) {
            IOutputTarget target = storage.getOutputTarget();
            IManifest manifest = workbook.getManifest();
            for (IFileEntry entry : manifest.getFileEntries()) {
                if (!entry.isDirectory()) {
                    String entryPath = entry.getPath();
                    if (shouldLoadEntry(entryPath)) {
                        copyEntry(source, target, entryPath);
                        markLoaded(entryPath);
                    }
                }
            }
        } else {
            // Prefetch all file entries:
            workbook.getManifest().getFileEntries();
        }
    }

    private boolean shouldLoadEntry(String entryPath) {
        return entryPath != null && !"".equals(entryPath) //$NON-NLS-1$
                && !hasBeenLoaded(entryPath);
    }

    private void copyEntry(IInputSource source, IOutputTarget target,
            String entryPath) throws IOException, CoreException {
        InputStream in = getInputStream(source, entryPath);
        if (in == null)
            return;

        try {
            long time = source.getEntryTime(entryPath);
            if (time >= 0)
                target.setEntryTime(entryPath, time);
            OutputStream out = getOutputStream(target, entryPath);
            try {
                int numBytes;
                while ((numBytes = in.read(byteBuffer)) > 0) {
                    out.write(byteBuffer, 0, numBytes);
                }
                if (!verifyChecksum(source, entryPath, in))
                    throw new CoreException(Core.ERROR_WRONG_PASSWORD);
            } finally {
                out.close();
            }

        } finally {
            in.close();
        }
    }

    private OutputStream getOutputStream(IOutputTarget target, String entryPath)
            throws IOException {
        if (!target.isEntryAvaialble(entryPath))
            return null;

        return target.openEntryStream(entryPath);
    }

    private void markLoaded(String entryPath) {
        loadedEntries.add(entryPath);
    }

    private boolean hasBeenLoaded(String entryPath) {
        return loadedEntries.contains(entryPath);
    }

    private void initWorkbook() throws IOException, CoreException {
        if (storage != null) {
            workbook.setTempStorage(storage);
        }
        initWorkbookContents(workbook);
        workbook.setPassword(password);
    }

    private void initWorkbookContents(WorkbookImpl workbook) {
        for (ISheet s : workbook.getSheets()) {
            initSheet(s, workbook);
        }
    }

    private void initSheet(ISheet sheet, WorkbookImpl wb) {
        ((SheetImpl) sheet).addNotify(wb);

        // Prefetch all revisions of this sheet.
        workbook.getRevisionRepository().getRevisionManager(sheet.getId(),
                IRevision.SHEET);
    }

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

    private Document forceLoadXML(String entryPath) throws IOException,
            CoreException {
        try {
            return loadXMLFile(source, entryPath);
        } catch (Throwable e) {
            if (e instanceof CoreException) {
                CoreException coreEx = (CoreException) e;
                if (coreEx.getType() == Core.ERROR_WRONG_PASSWORD
                        || coreEx.getType() == Core.ERROR_CANCELLATION) {
                    throw coreEx;
                }
            }
            //in case the file is damaged, 
            //try continue loading
            Core.getLogger().log(e, "Faild to load " + entryPath); //$NON-NLS-1$
            return createDocument();
        }
    }

    private InputStream getInputStream(IInputSource source, String entryPath)
            throws CoreException {
        if (!source.hasEntry(entryPath))
            return null;

        InputStream in = source.getEntryStream(entryPath);
        if (in == null)
            return null;

        if (manifest != null) {
            IEncryptionData encData = manifest.getEncryptionData(entryPath);
            if (encData != null) {
                in = createDecryptedStream(in, encData);
            }
        }

        return in;
    }

    private InputStream createDecryptedStream(InputStream in,
            IEncryptionData encData) throws CoreException {
        String password = getPassword();
        if (password == null)
            throw new CoreException(Core.ERROR_CANCELLATION);
        return Crypto.createInputStream(in, false, encData, password);
    }

    private String getPassword() throws CoreException {
        if (password == null) {
            if (encryptionHandler != null) {
                password = encryptionHandler.retrievePassword();
            }
        }
        return password;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.xmind.core.internal.dom.XMLLoader#doLoadXMLFile(org.xmind.core.io
     * .IInputSource, java.lang.String)
     */
    protected Document doLoadXMLFile(IInputSource source, String entryPath)
            throws IOException, CoreException {
        InputStream stream = getInputStream(source, entryPath);
        if (stream == null)
            throw new CoreException(Core.ERROR_NO_SUCH_ENTRY, entryPath);

        Document doc;
        try {
            doc = builder.getDocumentLoader().parse(stream);
        } catch (Throwable error) {
            if (!verifyChecksum(source, entryPath, stream))
                throw new CoreException(Core.ERROR_WRONG_PASSWORD, error);
            if (error instanceof IOException)
                throw (IOException) error;
            if (error instanceof CoreException)
                throw (CoreException) error;
            throw new CoreException(Core.ERROR_FAIL_PARSING_XML, error);
        } finally {
            stream.close();
        }

        if (!verifyChecksum(source, entryPath, stream))
            throw new CoreException(Core.ERROR_WRONG_PASSWORD);

        markLoaded(entryPath);

        return doc;
    }

    private boolean verifyChecksum(IInputSource source, String entryName,
            InputStream stream) throws IOException, CoreException {
        if (stream instanceof IChecksumStream) {
            if (manifest == null) {
                throw new IllegalStateException(
                        "Manifest should not be encrypted"); //$NON-NLS-1$
            }
            IEncryptionData encData = manifest.getEncryptionData(entryName);
            if (encData != null) {
                String expectedChecksum = encData.getChecksum();
                if (expectedChecksum != null) {
                    String actualChecksum;
                    actualChecksum = ((IChecksumStream) stream).getChecksum();
                    if (actualChecksum == null
                            || !expectedChecksum.equals(actualChecksum)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    public Document createDocument() {
        return builder.createDocument();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy