com.bdaum.zoom.operations.internal.ExportMetadataOperation.java Source code

Java tutorial

Introduction

Here is the source code for com.bdaum.zoom.operations.internal.ExportMetadataOperation.java

Source

/*
 * This file is part of the ZoRa project: http://www.photozora.org.
 *
 * ZoRa is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * ZoRa is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ZoRa; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * (c) 2009 Berthold Daum  
 */

package com.bdaum.zoom.operations.internal;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.osgi.util.NLS;

import com.adobe.xmp.XMPException;
import com.adobe.xmp.XMPMeta;
import com.adobe.xmp.XMPMetaFactory;
import com.bdaum.zoom.cat.model.asset.Asset;
import com.bdaum.zoom.cat.model.asset.AssetImpl;
import com.bdaum.zoom.core.Core;
import com.bdaum.zoom.core.IVolumeManager;
import com.bdaum.zoom.core.QueryField;
import com.bdaum.zoom.core.db.IDbErrorHandler;
import com.bdaum.zoom.core.internal.CoreActivator;
import com.bdaum.zoom.core.internal.FileWatchManager;
import com.bdaum.zoom.image.ImageConstants;
import com.bdaum.zoom.operations.DbOperation;
import com.bdaum.zoom.operations.IProfiledOperation;
import com.bdaum.zoom.operations.internal.xmp.XMPUtilities;
import com.bdaum.zoom.program.BatchUtilities;
import com.bdaum.zoom.program.DiskFullException;

@SuppressWarnings("restriction")
public class ExportMetadataOperation extends DbOperation {

    private List<Asset> assets;
    private boolean overwriteAll;
    private boolean ignoreAll;
    private final Set<QueryField> xmpFilter;
    private FileWatchManager fileWatcher = CoreActivator.getDefault().getFileWatchManager();
    private final boolean jpeg;
    private boolean silent;
    private File firstExport;
    private final boolean show;

    public ExportMetadataOperation(List<Asset> assets, Set<QueryField> xmpFilter, boolean jpeg, boolean silent,
            boolean show) {
        super(Messages.getString("ExportMetadataOperation.Export_metadata")); //$NON-NLS-1$
        this.assets = assets;
        this.xmpFilter = xmpFilter;
        this.jpeg = jpeg;
        this.silent = silent;
        this.show = show;
    }

    @Override
    public boolean isSilent() {
        return silent;
    }

    @Override
    public IStatus execute(IProgressMonitor aMonitor, final IAdaptable info) throws ExecutionException {
        try {
            final List<String> errands = new ArrayList<String>();
            final Set<String> volumes = new HashSet<String>();
            if (assets.isEmpty()) {
                List<AssetImpl> set = dbManager.obtainStruct(AssetImpl.class, null, false, null, null, false);
                init(aMonitor, set.size());
                for (AssetImpl asset : set) {
                    export(asset, aMonitor, set.size() > 1, info, errands, volumes);
                    if (aMonitor.isCanceled())
                        return abort();
                }
            } else {
                init(aMonitor, assets.size());
                for (Asset asset : assets) {
                    if (asset.getFileState() != IVolumeManager.PEER) {
                        export(asset, aMonitor, assets.size() > 1, info, errands, volumes);
                        if (aMonitor.isCanceled())
                            return abort();
                    }
                }
            }
            if (!errands.isEmpty()) {
                final IDbErrorHandler errorHandler = Core.getCore().getErrorHandler();
                if (errorHandler != null) {
                    String msg;
                    if (errands.size() == 1)
                        msg = NLS.bind(Messages.getString("ExportMetadataOperation.File_offline"), //$NON-NLS-1$
                                errands.get(0), volumes.toArray()[0]);
                    else {
                        StringBuffer sb = new StringBuffer();
                        for (String volume : volumes) {
                            if (sb.length() > 0)
                                sb.append(", "); //$NON-NLS-1$
                            sb.append(volume);
                        }
                        msg = NLS.bind(Messages.getString("ExportMetadataOperation.Files_offline"), //$NON-NLS-1$
                                errands.size(), sb.toString());

                    }
                    errorHandler.showInformation(Messages.getString("ExportMetadataOperation.Unable_to_export_XMP"), //$NON-NLS-1$
                            msg, info);
                }
            }
            return close(info);
        } finally {
            fileWatcher.stopIgnoring(opId);
            if (show && firstExport != null)
                BatchUtilities.showInFolder(firstExport);
        }
    }

    @SuppressWarnings("fallthrough")
    private void export(Asset asset, IProgressMonitor aMonitor, boolean multiple, IAdaptable info,
            List<String> errands, Set<String> volumes) {
        URI uri = Core.getCore().getVolumeManager().findFile(asset);
        if (uri != null) {
            boolean conflict = false;
            boolean saveOriginal = false;
            File xmpFile = null;
            File[] sidecars = Core.getSidecarFiles(uri, false);
            if (sidecars.length > 0) {
                xmpFile = sidecars[sidecars.length - 1];
                if (xmpFile.exists()) {
                    long lastModified = xmpFile.lastModified();
                    Date xmpModifiedAt = asset.getXmpModifiedAt();
                    conflict = xmpModifiedAt != null && lastModified > xmpModifiedAt.getTime();
                    saveOriginal = xmpModifiedAt == null
                            || xmpModifiedAt.getTime() < asset.getImportDate().getTime();
                    if (conflict) {
                        if (ignoreAll)
                            return;
                        if (!overwriteAll) {
                            int ret = 4;
                            IDbErrorHandler errorHandler = Core.getCore().getErrorHandler();
                            if (errorHandler != null)
                                ret = errorHandler.showMessageDialog(
                                        Messages.getString("ExportMetadataOperation.XMP_conflict"), //$NON-NLS-1$
                                        null,
                                        NLS.bind(Messages.getString("ExportMetadataOperation.XMP_out_of_sync"), //$NON-NLS-1$
                                                xmpFile),
                                        MessageDialog.QUESTION,
                                        (multiple)
                                                ? new String[] {
                                                        Messages.getString("ExportMetadataOperation.Overwrite"), //$NON-NLS-1$
                                                        Messages.getString("ExportMetadataOperation.Overwrite_all"), //$NON-NLS-1$
                                                        IDialogConstants.SKIP_LABEL,
                                                        Messages.getString("ExportMetadataOperation.Skip_all"), //$NON-NLS-1$
                                                        Messages.getString("ExportMetadataOperation.Cancel") } //$NON-NLS-1$
                                                : new String[] {
                                                        Messages.getString("ExportMetadataOperation.Overwrite"), //$NON-NLS-1$
                                                        IDialogConstants.CANCEL_LABEL },
                                        0, info);
                            if (multiple) {
                                switch (ret) {
                                case 1:
                                    overwriteAll = true;
                                    /* FALL-THROUGH */
                                case 0:
                                    break;
                                case 3:
                                    ignoreAll = true;
                                    /* FALL-THROUGH */
                                case 2:
                                    return;
                                default:
                                    aMonitor.setCanceled(true);
                                    return;
                                }
                            } else {
                                switch (ret) {
                                case 0:
                                    break;
                                default:
                                    aMonitor.setCanceled(true);
                                    return;
                                }
                            }
                        }
                    }
                }
            }
            for (int i = 0; i < sidecars.length; i++) {
                xmpFile = sidecars[i];
                if (xmpFile.exists() || i == sidecars.length - 1) {
                    File newFile = new File(xmpFile.getAbsoluteFile() + ".new"); //$NON-NLS-1$
                    fileWatcher.ignore(newFile, opId);
                    fileWatcher.ignore(xmpFile, opId);
                    newFile.delete();
                    try {
                        XMPUtilities.configureXMPFactory();
                        XMPMeta xmpMeta;
                        if (xmpFile.exists())
                            try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(xmpFile))) {
                                xmpMeta = XMPMetaFactory.parse(in);
                            } catch (XMPException e) {
                                addError(NLS.bind(Messages.getString("ExportMetadataOperation.invalid_xmp"), //$NON-NLS-1$
                                        xmpFile), e);
                                xmpMeta = XMPMetaFactory.create();
                            }
                        else
                            xmpMeta = XMPMetaFactory.create();
                        try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(newFile))) {
                            XMPUtilities.writeProperties(xmpMeta, asset, xmpFilter, false);
                            XMPMetaFactory.serialize(xmpMeta, out);
                            if (jpeg && ImageConstants.isJpeg(Core.getFileExtension(uri.toString()))) {
                                File imageFile = new File(uri);
                                try (BufferedInputStream in1 = new BufferedInputStream(
                                        new FileInputStream(imageFile))) {
                                    byte[] bytes = new byte[(int) imageFile.length()];
                                    if (in1.read(bytes) > 0)
                                        try {
                                            byte[] oldXmp = XMPUtilities.getXmpFromJPEG(bytes);
                                            if (oldXmp != null)
                                                try (BufferedInputStream in = new BufferedInputStream(
                                                        new ByteArrayInputStream(oldXmp))) {
                                                    xmpMeta = XMPMetaFactory.parse(in);
                                                } catch (XMPException e) {
                                                    addError(NLS.bind(
                                                            Messages.getString(
                                                                    "ExportMetadataOperation.invalid_inline_xmp"), //$NON-NLS-1$
                                                            imageFile), e);
                                                    xmpMeta = XMPMetaFactory.create();
                                                }
                                            else
                                                xmpMeta = XMPMetaFactory.create();
                                            ByteArrayOutputStream mout = new ByteArrayOutputStream();
                                            XMPUtilities.writeProperties(xmpMeta, asset, xmpFilter, false);
                                            XMPMetaFactory.serialize(xmpMeta, mout);
                                            bytes = XMPUtilities.insertXmpIntoJPEG(bytes, mout.toByteArray());
                                        } catch (XMPException e) {
                                            addError(NLS.bind(
                                                    Messages.getString("ExportMetadataOperation.unable_to_export"), //$NON-NLS-1$
                                                    imageFile), e);
                                        }
                                    fileWatcher.ignore(imageFile, opId);
                                    try (BufferedOutputStream out1 = new BufferedOutputStream(
                                            new FileOutputStream(imageFile))) {
                                        out1.write(bytes);
                                    }
                                }
                            }
                        }
                    } catch (IOException e) {
                        addError(Messages.getString("ExportMetadataOperation.IO_error_creating_XMP"), e); //$NON-NLS-1$
                    } catch (XMPException e) {
                        addError(Messages.getString("ExportMetadataOperation.XMP_parsing_error"), //$NON-NLS-1$
                                e);
                    }
                    if (newFile.exists()) {
                        try {
                            if (saveOriginal) {
                                File backupFile = new File(xmpFile.getAbsolutePath() + ".original"); //$NON-NLS-1$
                                if (!backupFile.exists()) {
                                    fileWatcher.ignore(backupFile, opId);
                                    BatchUtilities.moveFile(xmpFile, backupFile, aMonitor);
                                }
                            }
                            BatchUtilities.moveFile(newFile, xmpFile, aMonitor);
                            asset.setXmpModifiedAt(new Date(xmpFile.lastModified()));
                            storeSafely(null, 1, asset);
                        } catch (IOException e) {
                            Core.getCore().logError(
                                    Messages.getString("ExportMetadataOperation.IO_error_exporting_metadata"), e); //$NON-NLS-1$
                        } catch (DiskFullException e) {
                            Core.getCore().logError(
                                    Messages.getString("ExportMetadataOperation.IO_error_exporting_metadata"), e); //$NON-NLS-1$
                        }
                        if (firstExport == null)
                            firstExport = xmpFile;
                    }
                }
            }
        } else {
            String volume = asset.getVolume();
            if (volume != null && !volume.isEmpty())
                volumes.add(volume);
            errands.add(asset.getUri());
        }
    }

    @Override
    public boolean canRedo() {
        return false;
    }

    @Override
    public IStatus redo(IProgressMonitor aMonitor, IAdaptable info) throws ExecutionException {
        return null;
    }

    @Override
    public boolean canUndo() {
        return false;
    }

    @Override
    public IStatus undo(IProgressMonitor aMonitor, IAdaptable info) throws ExecutionException {
        return null;
    }

    public int getExecuteProfile() {
        return IProfiledOperation.CONTENT | IProfiledOperation.XMP;
    }

    public int getUndoProfile() {
        return 0;
    }

    @Override
    public int getPriority() {
        return assets.size() > 3 ? Job.LONG : Job.SHORT;
    }

}