net.sourceforge.javahexeditor.plugin.editors.HexEditor.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.javahexeditor.plugin.editors.HexEditor.java

Source

/*
 * javahexeditor, a java hex editor
 * Copyright (C) 2006, 2009 Jordi Bergenthal, pestatije(-at_)users.sourceforge.net
 * The official javahexeditor site is sourceforge.net/projects/javahexeditor
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package net.sourceforge.javahexeditor.plugin.editors;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.HashSet;
import java.util.Set;

import net.sourceforge.javahexeditor.BinaryContent;
import net.sourceforge.javahexeditor.BinaryContent.RangeSelection;
import net.sourceforge.javahexeditor.HexTexts;
import net.sourceforge.javahexeditor.Manager;
import net.sourceforge.javahexeditor.Preferences;
import net.sourceforge.javahexeditor.Texts;
import net.sourceforge.javahexeditor.common.Log;
import net.sourceforge.javahexeditor.common.TextUtility;
import net.sourceforge.javahexeditor.plugin.HexEditorPlugin;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.editors.text.ILocationProvider;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;

public final class HexEditor extends EditorPart implements ISelectionProvider {

    private static class MyAction extends Action {
        private Manager manager;
        private String myId;

        public MyAction(Manager manager, String id) {
            if (manager == null) {
                throw new IllegalArgumentException("Parameter 'manager' must not be null.");
            }
            if (id == null) {
                throw new IllegalArgumentException("Parameter 'id' must not be null.");
            }
            this.manager = manager;
            myId = id;
        }

        @Override
        public void run() {
            if (myId.equals(ActionFactory.UNDO.getId()))
                manager.doUndo();
            else if (myId.equals(ActionFactory.REDO.getId()))
                manager.doRedo();
            else if (myId.equals(ActionFactory.CUT.getId()))
                manager.doCut();
            else if (myId.equals(ActionFactory.COPY.getId()))
                manager.doCopy();
            else if (myId.equals(ActionFactory.PASTE.getId()))
                manager.doPaste();
            else if (myId.equals(ActionFactory.DELETE.getId()))
                manager.doDelete();
            else if (myId.equals(ActionFactory.SELECT_ALL.getId()))
                manager.doSelectAll();
            else if (myId.equals(ActionFactory.FIND.getId()))
                manager.doFind();
        }
    }

    // Public id from the contributions.
    public static final String ID = "net.sourceforge.javahexeditor";

    // Private ids from the contributions.
    private static final String OUTLINE_ELEMENT_ATTRIBUTE_CLASS = "class";
    private static final String OUTLINE_ELEMENT_NAME = "outline";
    private static final String OUTLINE_ID = "net.sourceforge.javahexeditor.outline";

    Manager manager;
    private IContentOutlinePage outlinePage;
    private IPropertyChangeListener preferencesChangeListener;
    Set<ISelectionChangedListener> selectionListeners;

    private IStatusLineManager statusLineManager;

    private HexTexts hexTexts;

    public HexEditor() {
        super();
        manager = new Manager();
    }

    @Override
    public void addSelectionChangedListener(ISelectionChangedListener listener) {
        if (listener == null) {
            return;
        }

        if (selectionListeners == null) {
            selectionListeners = new HashSet<ISelectionChangedListener>();
        }
        selectionListeners.add(listener);
    }

    @Override
    public void createPartControl(Composite parent) {

        statusLineManager = getEditorSite().getActionBars().getStatusLineManager();

        HexEditorPlugin plugin = HexEditorPlugin.getDefault();
        getManager().setTextFont(HexEditorPreferences.getFontData());
        manager.setFindReplaceHistory(plugin.getFindReplaceHistory());
        hexTexts = manager.createEditorPart(parent);
        FillLayout fillLayout = new FillLayout();
        parent.setLayout(fillLayout);

        String charset = null;
        IEditorInput unresolved = getEditorInput();
        File systemFile = null;
        IFile localFile = null;
        String inputName = null;
        if (unresolved instanceof FileEditorInput) {
            localFile = ((FileEditorInput) unresolved).getFile();
        } else if (unresolved instanceof IPathEditorInput) { // eg.
            // FileInPlaceEditorInput
            IPathEditorInput file = (IPathEditorInput) unresolved;
            systemFile = file.getPath().toFile();
        } else if (unresolved instanceof ILocationProvider) {
            ILocationProvider location = (ILocationProvider) unresolved;
            IWorkspaceRoot rootWorkspace = ResourcesPlugin.getWorkspace().getRoot();
            localFile = rootWorkspace.getFile(location.getPath(location));
        } else if (unresolved instanceof IURIEditorInput) {
            URI uri = ((IURIEditorInput) unresolved).getURI();
            if (uri != null) {
                IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
                IFile[] files = root.findFilesForLocationURI(uri);

                if (files.length != 0) {
                    localFile = files[0];
                } else {
                    systemFile = new File(uri);
                }
            }
        } else if (unresolved instanceof IStorageEditorInput) {
            // TODO this is just a hack to get the editor opened on history entries
            // Ideally we would open not files but streams.
            IStorageEditorInput input = (IStorageEditorInput) unresolved;
            try {
                Path tmpDir = Files.createTempDirectory(null);
                Path tmpFile = tmpDir.resolve(input.getStorage().getName());
                systemFile = tmpFile.toFile();
                systemFile.createNewFile();
                systemFile.deleteOnExit();
                Files.copy(input.getStorage().getContents(), systemFile.toPath(),
                        StandardCopyOption.REPLACE_EXISTING);
                if (input.getClass().getName().contains("FileRevisionEditorInput")) {
                    inputName = tmpFile.getFileName() + " (from history)";
                } else {
                    inputName = null;
                }
            } catch (Exception e) {
                HexEditorPlugin.logError("Error on opening: " + localFile, e);
            }
        }
        // charset
        if (localFile != null) {
            systemFile = localFile.getLocation().toFile();
            try {
                charset = localFile.getCharset(true);
            } catch (CoreException e) {
                HexEditorPlugin.logError("Error on opening: " + localFile, e);
            }
        }

        if (systemFile != null) {
            try {
                // open file
                manager.openFile(systemFile, charset);
            } catch (Exception ex) {
                HexEditorPlugin.logError("Error on opening: " + localFile, ex);
                statusLineManager.setErrorMessage(ex.getMessage());
                systemFile = null;
            }
            if (inputName == null) {
                inputName = systemFile.getName();
            }
            setPartName(inputName);
        } else {
            setPartName(Texts.EMPTY);
        }

        // Register any global actions with the site's IActionBars.
        IActionBars bars = getEditorSite().getActionBars();
        String id = ActionFactory.UNDO.getId();
        bars.setGlobalActionHandler(id, new MyAction(manager, id));
        id = ActionFactory.REDO.getId();
        bars.setGlobalActionHandler(id, new MyAction(manager, id));
        id = ActionFactory.CUT.getId();
        bars.setGlobalActionHandler(id, new MyAction(manager, id));
        id = ActionFactory.COPY.getId();
        bars.setGlobalActionHandler(id, new MyAction(manager, id));
        id = ActionFactory.PASTE.getId();
        bars.setGlobalActionHandler(id, new MyAction(manager, id));
        id = ActionFactory.DELETE.getId();
        bars.setGlobalActionHandler(id, new MyAction(manager, id));
        id = ActionFactory.SELECT_ALL.getId();
        bars.setGlobalActionHandler(id, new MyAction(manager, id));
        id = ActionFactory.FIND.getId();
        bars.setGlobalActionHandler(id, new MyAction(manager, id));

        manager.addListener(new Listener() {
            @Override
            @SuppressWarnings("synthetic-access")
            public void handleEvent(Event event) {
                firePropertyChange(PROP_DIRTY);
                updateActionsStatus();
            }
        });

        bars.updateActionBars();

        preferencesChangeListener = new IPropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent event) {
                if (Preferences.FONT_DATA.equals(event.getProperty())) {
                    manager.setTextFont((FontData) event.getNewValue());
                }
            }
        };
        IPreferenceStore store = plugin.getPreferenceStore();
        store.addPropertyChangeListener(preferencesChangeListener);

        manager.addLongSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Log.trace(this, "Long selection: {0}", e);
                if (selectionListeners == null) {
                    return;
                }

                long[] longSelection = manager.getLongSelection(e);
                SelectionChangedEvent event = new SelectionChangedEvent(HexEditor.this, new StructuredSelection(
                        new Object[] { new Long(longSelection[0]), new Long(longSelection[1]) }));
                for (ISelectionChangedListener listener : selectionListeners) {
                    listener.selectionChanged(event);
                }
            }
        });
        // getSite().getPage().addSelectionListener(
        // new ISelectionListener() {
        // @Override
        // public void selectionChanged(IWorkbenchPart part,
        // ISelection selection) {
        // if (ID.equals(part.getSite().getId())) {
        // return;
        // }
        // }
        // });
    }

    @Override
    public void dispose() {
        IPreferenceStore store = HexEditorPlugin.getDefault().getPreferenceStore();
        if (preferencesChangeListener != null) {
            store.removePropertyChangeListener(preferencesChangeListener);
        }
        if (hexTexts != null) {
            hexTexts.dispose();
        }
    }

    @Override
    public void doSave(IProgressMonitor monitor) {
        monitor.beginTask(Texts.EDITOR_MESSAGE_SAVING_FILE_PLEASE_WAIT, IProgressMonitor.UNKNOWN);
        try {
            getManager().saveFile(monitor);
        } catch (IOException ex) {
            statusLineManager.setErrorMessage(ex.getMessage());
        }
        monitor.done();
    }

    @Override
    public void doSaveAs() {
        saveToFile(false);
    }

    @Override
    public Object getAdapter(@SuppressWarnings("rawtypes") Class required) {
        Object result = null;
        if (IContentOutlinePage.class.isAssignableFrom(required)) {
            if (outlinePage == null) {
                outlinePage = getOutlinePage();
            }
            result = outlinePage;
        } else if (BinaryContent.class.isAssignableFrom(required)) {
            result = getManager().getContent();
        } else if (Manager.class.isAssignableFrom(required)) {
            result = getManager();
        } else {
            result = super.getAdapter(required);
        }
        return result;
    }

    /**
     * Getter for the manager instance.
     *
     * @return the manager
     */
    public Manager getManager() {
        return manager;
    }

    IContentOutlinePage getOutlinePage() {
        IExtensionRegistry registry = Platform.getExtensionRegistry();
        IExtensionPoint point = registry.getExtensionPoint(OUTLINE_ID);
        if (point == null) {
            return null;
        }

        IExtension[] extensions = point.getExtensions();
        if (extensions.length == 0) {
            return null;
        }
        IConfigurationElement[] elements = extensions[0].getConfigurationElements();
        String className = null;
        for (int i = 0; i < elements.length; ++i) {
            if (OUTLINE_ELEMENT_NAME.equals(elements[i].getName())) {
                className = elements[i].getAttribute(OUTLINE_ELEMENT_ATTRIBUTE_CLASS);
                break;
            }
        }

        Bundle aBundle = Platform.getBundle(extensions[0].getNamespaceIdentifier());
        IContentOutlinePage result = null;
        if (aBundle != null) {
            try {
                aBundle.start();
            } catch (BundleException e) {
                return null;
            }
            try {
                // throws IllegalAccessException, InstantiationException,
                // ClassNotFoundException
                result = (IContentOutlinePage) aBundle.loadClass(className).newInstance();
            } catch (Exception e) {
                return null;
            }
        }

        return result;
    }

    @Override
    public ISelection getSelection() {
        RangeSelection rangeSelection = getManager().getSelection();
        return new StructuredSelection(
                new Object[] { new Long(rangeSelection.start), new Long(rangeSelection.end) });
    }

    @Override
    public void init(IEditorSite site, final IEditorInput input) throws PartInitException {
        Log.trace(this, "init starts with selection provider {0}", site.getSelectionProvider());

        setSite(site);
        if (!(input instanceof IPathEditorInput) && !(input instanceof ILocationProvider)
                && (!(input instanceof IURIEditorInput)) && (!(input instanceof IStorageEditorInput))) {
            throw new PartInitException("Input '" + input.toString() + "'is not a file");
        }
        setInput(input);
        // When opening an external file the workbench (Eclipse 3.1) calls
        // HexEditorActionBarContributor.
        // MyStatusLineContributionItem.fill() before
        // HexEditorActionBarContributor.setActiveEditor()
        // but we need an editor to fill the status bar.
        site.getActionBarContributor().setActiveEditor(this);
        site.setSelectionProvider(this);
    }

    @Override
    public boolean isDirty() {
        return getManager().isDirty();
    }

    @Override
    public boolean isSaveAsAllowed() {
        return true;
    }

    @Override
    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
        if (selectionListeners != null) {
            selectionListeners.remove(listener);
        }
    }

    void saveToFile(final boolean selection) {
        final File file = getManager().showSaveAsDialog(getEditorSite().getShell(), selection);
        if (file == null) {
            return;
        }

        IRunnableWithProgress runnable = new IRunnableWithProgress() {
            @Override
            public void run(IProgressMonitor monitor) {
                saveToFile(file, selection, monitor);
            }
        };
        ProgressMonitorDialog monitorDialog = new ProgressMonitorDialog(getEditorSite().getShell());
        try {
            monitorDialog.run(false, false, runnable);
        } catch (InvocationTargetException ex) {
            throw new RuntimeException(ex);
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }
    }

    void saveToFile(File file, boolean selection, IProgressMonitor monitor) {
        monitor.beginTask(Texts.EDITOR_MESSAGE_SAVING_FILE_PLEASE_WAIT, IProgressMonitor.UNKNOWN);
        try {
            if (selection) {
                manager.doSaveSelectionAs(file);
            } else {
                manager.saveAsFile(file, monitor);
            }
        } catch (IOException ex) {
            monitor.done();
            statusLineManager.setErrorMessage(ex.getMessage());
            return;
        }
        monitor.done();
        if (!selection) {
            setPartName(file.getName());
            firePropertyChange(PROP_DIRTY);
        }

        statusLineManager.setMessage(TextUtility.format(Texts.EDITOR_MESSAGE_FILE_SAVED, file.getAbsolutePath()));
    }

    @Override
    public void setFocus() {
        // useless. It is called before ActionBarContributor.setActiveEditor()
        // so focusing is done there
        hexTexts.setFocus();
    }

    @Override
    public void setSelection(ISelection selection) {
        if (selection.isEmpty()) {
            return;
        }
        StructuredSelection aSelection = (StructuredSelection) selection;
        long[] startEnd = (long[]) aSelection.getFirstElement();
        long start = startEnd[0];
        long end = start;
        if (startEnd.length > 1) {
            end = startEnd[1];
        }
        if (aSelection.size() > 1) {
            startEnd = (long[]) aSelection.toArray()[1];
            end = startEnd[0];
            if (startEnd.length > 1) {
                end = startEnd[1];
            }
        }
        getManager().setSelection(new RangeSelection(start, end));
    }

    /**
     * Updates the status of actions: enables/disables them depending on whether
     * there is text selected and whether inserting or overwriting is active.
     * Undo/redo actions are enabled/disabled as well.
     */
    void updateActionsStatus() {
        boolean textSelected = getManager().isTextSelected();
        boolean lengthModifiable = textSelected && !manager.isOverwriteMode();
        IActionBars bars = getEditorSite().getActionBars();
        IAction action = bars.getGlobalActionHandler(ActionFactory.UNDO.getId());
        if (action != null) {
            action.setEnabled(manager.canUndo());
        }

        action = bars.getGlobalActionHandler(ActionFactory.REDO.getId());
        if (action != null) {
            action.setEnabled(manager.canRedo());
        }

        action = bars.getGlobalActionHandler(ActionFactory.CUT.getId());
        if (action != null) {
            action.setEnabled(lengthModifiable);
        }

        action = bars.getGlobalActionHandler(ActionFactory.COPY.getId());
        if (action != null) {
            action.setEnabled(textSelected);
        }

        action = bars.getGlobalActionHandler(ActionFactory.DELETE.getId());
        if (action != null) {
            action.setEnabled(lengthModifiable);
        }

        bars.updateActionBars();
    }
}