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

gate.gui.DocumentExportMenu Maven / Gradle / Ivy

Go to download

GATE - general achitecture for text engineering - is open source software capable of solving almost any text processing problem. This artifact enables you to embed the core GATE Embedded with its essential dependencies. You will able to use the GATE Embedded API and load and store GATE XML documents. This artifact is the perfect dependency for CREOLE plugins or for applications that need to customize the GATE dependencies due to confict with their own dependencies or for lower footprint.

The newest version!
/*
 *  Copyright (c) 1995-2014, The University of Sheffield. See the file
 *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
 *
 *  This file is part of GATE (see http://gate.ac.uk/), and is free
 *  software, licenced under the GNU Library General Public License,
 *  Version 2, June 1991 (in the distribution as file licence.html,
 *  and also available at http://gate.ac.uk/gate/licence.html).
 *
 *  Mark A. Greenwood 11/07/2014
 *
 */

package gate.gui;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.table.TableCellEditor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import gate.Corpus;
import gate.CorpusExporter;
import gate.Document;
import gate.DocumentExporter;
import gate.Factory;
import gate.FeatureMap;
import gate.Gate;
import gate.Resource;
import gate.Utils;
import gate.corpora.export.GateXMLExporter;
import gate.creole.Parameter;
import gate.creole.ParameterException;
import gate.creole.ResourceData;
import gate.event.CreoleEvent;
import gate.event.CreoleListener;
import gate.swing.XJFileChooser;
import gate.swing.XJMenu;
import gate.util.Err;
import gate.util.Files;
import java.text.NumberFormat;

/**
 * A menu which updates as plugins are (un)loaded to allow the export of
 * documents and corpora to any of the supported output formats.
 */
@SuppressWarnings("serial")
public class DocumentExportMenu extends XJMenu implements CreoleListener {

  private static final Logger log = LoggerFactory.getLogger(DocumentExportMenu.class);

  static DocumentExportDialog dialog = new DocumentExportDialog();

  protected IdentityHashMap itemByResource =
          new IdentityHashMap();

  private Handle handle;

  public DocumentExportMenu(NameBearerHandle handle) {
    super("Save as...", "", handle.sListenerProxy);
    if(!(handle.getTarget() instanceof Document)
            && !(handle.getTarget() instanceof Corpus))
      throw new IllegalArgumentException(
              "We only deal with documents and corpora");
    this.handle = handle;
    init();
  }

  private void init() {

    DocumentExporter gateXMLExporter =
            (DocumentExporter)Gate.getCreoleRegister()
                    .get(GateXMLExporter.class.getCanonicalName())
                    .getInstantiations().iterator().next();
    addExporter(gateXMLExporter);

    Set toolTypes = Gate.getCreoleRegister().getToolTypes();
    for(String type : toolTypes) {
      List instances =
              Gate.getCreoleRegister().get(type).getInstantiations();
      for(Resource res : instances) {
        if(res instanceof DocumentExporter) {
          addExporter((DocumentExporter)res);
        }
      }
    }
    Gate.getCreoleRegister().addCreoleListener(this);
  }

  private File getSelectedFile(List> params,
          DocumentExporter de, FeatureMap options) {
    File selectedFile = null;

    Document document =
            (handle.getTarget() instanceof Document ? (Document)handle
                    .getTarget() : null);
    // are we looking for a file or a directory?
    boolean singleFile = (document != null) || (de instanceof CorpusExporter);

    if(document != null && document.getSourceUrl() != null) {
      // there is a document and it has a URL so let's try and figure out a
      // sensible filename for it

      try {
        // firstly is the source URL actually a file? if so just use it
        selectedFile = Files.fileFromURL(document.getSourceUrl());
      } catch(IllegalArgumentException e) {
        // if the URL isn't a file then...

        String fileName = null;
        if(document.getNamedAnnotationSets().containsKey("Original markups")
            && !document.getAnnotations("Original markups").get("title")
                .isEmpty()) {
          // use the title annotation from the document
          fileName = Utils.stringFor(document,
              document.getAnnotations("Original markups").get("title"));
        }

        if (fileName == null) {
          // if there was no title then see if we can get a file name from the URL path
          String path = document.getSourceUrl().getPath();
          if (!path.isEmpty()) {
            // this may return the empty string but it should always be a valid
            // substring call
            fileName = path.substring(1+path.lastIndexOf("/"));
          }
        }

        if (fileName == null) {
          // if we still fail then use the document name
          fileName = document.getName();
        }

        // just to be on the safe side replace any odd characters with
        // underscores before trying to create a file
        fileName = fileName.replaceAll("[^/a-zA-Z0-9._-]+", "_");
        fileName = fileName.replaceAll("__+", "_");

        selectedFile = new File(fileName);
      }

      // get just the filename so we can mess about with its extension
      String fileName = selectedFile.getName();

      // if the file has an extension already replace it with the default for
      // this exporter
      fileName = fileName.replaceAll("\\.[a-zA-Z]{1,4}$",
          "." + de.getDefaultExtension());

      // if the file still doesn't end with the right extension then append it
      // to the end and be done with it
      if(!fileName.endsWith("." + de.getDefaultExtension())) {
        fileName += "." + de.getDefaultExtension();
      }

      //finally create the File object
      selectedFile = new File(selectedFile, fileName);

    }

    if(params == null || params.isEmpty()) {
      JFileChooser fileChooser = new JFileChooser();
      fileChooser.resetChoosableFileFilters();
      fileChooser.setFileFilter(de.getFileFilter());
      fileChooser.setMultiSelectionEnabled(false);
      fileChooser.setDialogTitle("Save as " + de.getFileType());
      fileChooser.setFileSelectionMode(singleFile
              ? JFileChooser.FILES_ONLY
              : JFileChooser.DIRECTORIES_ONLY);

      if(selectedFile != null) {
        fileChooser.ensureFileIsVisible(selectedFile);
        fileChooser.setSelectedFile(selectedFile);
      }      

      if(fileChooser.showSaveDialog(MainFrame.getInstance()) != JFileChooser.APPROVE_OPTION)
        return null;
      selectedFile = fileChooser.getSelectedFile();
    } else {
      if(!dialog.show(de, params, singleFile, selectedFile != null
              ? selectedFile.getAbsolutePath()
              : "")) return null;

      options.putAll(dialog.getSelectedParameters());
      selectedFile = new File(dialog.getSelectedFileName());
    }

    return selectedFile;
  }

  private void addExporter(final DocumentExporter de) {

    if(itemByResource.containsKey(de)) return;

    final ResourceData rd =
            Gate.getCreoleRegister().get(de.getClass().getCanonicalName());

    if(DocumentExportMenu.this.getItemCount() == 1) {
      DocumentExportMenu.this.addSeparator();
    }

    JMenuItem item =
            DocumentExportMenu.this.add(new AbstractAction(de.getFileType()
                    + " (." + de.getDefaultExtension() + ")", MainFrame
                    .getIcon(rd.getIcon(),rd.getResourceClassLoader())) {

              @Override
              public void actionPerformed(ActionEvent ae) {

                List> params =
                        rd.getParameterList().getRuntimeParameters();

                final FeatureMap options = Factory.newFeatureMap();

                final File selectedFile = getSelectedFile(params, de, options);

                if(selectedFile == null) return;

                Runnable runnable = new Runnable() {
                  public void run() {

                    if(handle.getTarget() instanceof Document) {

                      long start = System.currentTimeMillis();
                      listener.statusChanged("Saving as " + de.getFileType()
                              + " to " + selectedFile.toString() + "...");
                      try {
                        de.export((Document)handle.getTarget(), selectedFile,
                                options);
                      } catch(IOException e) {
                        e.printStackTrace();
                      }

                      long time = System.currentTimeMillis() - start;
                      listener.statusChanged("Finished saving as "
                              + de.getFileType() + " into " + " the file: "
                              + selectedFile.toString() + " in "
                              + ((double)time) / 1000 + "s");
                    } else { // corpus
                      if(de instanceof CorpusExporter) {

                        long start = System.currentTimeMillis();
                        listener.statusChanged("Saving as " + de.getFileType()
                                + " to " + selectedFile.toString() + "...");
                        try {
                          ((CorpusExporter)de).export((Corpus)handle.getTarget(), selectedFile,
                                  options);
                        } catch(IOException e) {
                          e.printStackTrace();
                        }

                        long time = System.currentTimeMillis() - start;
                        listener.statusChanged("Finished saving as "
                                + de.getFileType() + " into " + " the file: "
                                + selectedFile.toString() + " in "
                                + ((double)time) / 1000 + "s");
                      } else { // not a CorpusExporter
                        try {
                          File dir = selectedFile;
                          // create the top directory if needed
                          if(!dir.exists()) {
                            if(!dir.mkdirs()) {
                              JOptionPane.showMessageDialog(
                                      MainFrame.getInstance(),
                                      "Could not create top directory!", "GATE",
                                      JOptionPane.ERROR_MESSAGE);
                              return;
                            }
                          }
  
                          MainFrame.lockGUI("Saving...");
                          Corpus corpus = (Corpus)handle.getTarget();
  
                          long startTime = System.currentTimeMillis();
                          // iterate through all the docs and save
                          // each of
                          // them
                          Iterator docIter = corpus.iterator();
                          boolean overwriteAll = false;
                          int docCnt = corpus.size();
                          int currentDocIndex = 0;
                          Set usedFileNames = new HashSet();
                          while(docIter.hasNext()) {
                            boolean docWasLoaded =
                                    corpus.isDocumentLoaded(currentDocIndex);
                            Document currentDoc = docIter.next();
  
                            URL sourceURL = currentDoc.getSourceUrl();
                            String fileName = null;
                            if(sourceURL != null) {
                              fileName = sourceURL.getPath();
                              fileName = Files.getLastPathComponent(fileName);
                            }
                            if(fileName == null || fileName.length() == 0) {
                              fileName = currentDoc.getName();
                            }
                            // makes sure that the filename does not
                            // contain
                            // any
                            // forbidden character
                            fileName =
                                    fileName.replaceAll("[\\/:\\*\\?\"<>|]", "_");
                            if(fileName.toLowerCase().endsWith(
                                    "." + de.getDefaultExtension())) {
                              fileName =
                                      fileName.substring(0,
                                              fileName.length()
                                                      - de.getDefaultExtension()
                                                              .length()
                                                      - 1);
                            }
                            if(usedFileNames.contains(fileName)) {
                              // name clash -> add unique ID
                              String fileNameBase = fileName;
                              int uniqId = 0;
                              fileName = fileNameBase + "-" + uniqId++;
                              while(usedFileNames.contains(fileName)) {
                                fileName = fileNameBase + "-" + uniqId++;
                              }
                            }
                            usedFileNames.add(fileName);
                            if(!fileName.toLowerCase().endsWith(
                                    "." + de.getDefaultExtension()))
                              fileName += "." + de.getDefaultExtension();
                            File docFile = null;
                            boolean nameOK = false;
                            do {
                              docFile = new File(dir, fileName);
                              if(docFile.exists() && !overwriteAll) {
                                // ask the user if we can overwrite
                                // the file
                                Object[] opts =
                                        new Object[] {"Yes", "All", "No",
                                            "Cancel"};
                                MainFrame.unlockGUI();
                                int answer =
                                        JOptionPane.showOptionDialog(
                                                MainFrame.getInstance(), "File "
                                                        + docFile.getName()
                                                        + " already exists!\n"
                                                        + "Overwrite?", "GATE",
                                                JOptionPane.DEFAULT_OPTION,
                                                JOptionPane.WARNING_MESSAGE,
                                                null, opts, opts[2]);
                                MainFrame.lockGUI("Saving...");
                                switch(answer) {
                                  case 0: {
                                    nameOK = true;
                                    break;
                                  }
                                  case 1: {
                                    nameOK = true;
                                    overwriteAll = true;
                                    break;
                                  }
                                  case 2: {
                                    // user said NO, allow them to
                                    // provide
                                    // an
                                    // alternative name;
                                    MainFrame.unlockGUI();
                                    fileName =
                                            (String)JOptionPane.showInputDialog(
                                                    MainFrame.getInstance(),
                                                    "Please provide an alternative file name",
                                                    "GATE",
                                                    JOptionPane.QUESTION_MESSAGE,
                                                    null, null, fileName);
                                    if(fileName == null) {
                                      handle.processFinished();
                                      return;
                                    }
                                    MainFrame.lockGUI("Saving");
                                    break;
                                  }
                                  case 3: {
                                    // user gave up; return
                                    handle.processFinished();
                                    return;
                                  }
                                }
  
                              } else {
                                nameOK = true;
                              }
                            } while(!nameOK);
                            // save the file
                            try {
                              // do the actual exporting
                              de.export(currentDoc, docFile, options);
                            } catch(Exception ioe) {
                              MainFrame.unlockGUI();
                              JOptionPane.showMessageDialog(
                                      MainFrame.getInstance(),
                                      "Could not create write file:"
                                              + ioe.toString(), "GATE",
                                      JOptionPane.ERROR_MESSAGE);
                              ioe.printStackTrace(Err.getPrintWriter());
                              return;
                            }
  
                            listener.statusChanged(currentDoc.getName()
                                    + " saved");
                            // close the doc if it wasn't already
                            // loaded
                            if(!docWasLoaded) {
                              corpus.unloadDocument(currentDoc);
                              Factory.deleteResource(currentDoc);
                            }
  
                            handle.progressChanged(100 * currentDocIndex++
                                    / docCnt);
                          }// while(docIter.hasNext())
                          long endTime = System.currentTimeMillis();
                          listener.statusChanged("Corpus saved in "+ 
                                  NumberFormat.getInstance().format(
                                    (double)(endTime - startTime) / 1000)+
                                  " seconds!");
                          handle.processFinished();
                        } finally {
                          MainFrame.unlockGUI();
                        }
                      }
                    }
                  }
                };

                Thread thread =
                        new Thread(Thread.currentThread().getThreadGroup(),
                                runnable, "Document Exporter Thread");
                thread.setPriority(Thread.MIN_PRIORITY);
                thread.start();
              }

            });

    itemByResource.put(de, item);

  }

  /**
   * If the resource just loaded is a tool (according to the creole
   * register) then see if it publishes any actions and if so, add them
   * to the menu in the appropriate places.
   */
  @Override
  public void resourceLoaded(CreoleEvent e) {
    final Resource res = e.getResource();

    if(res instanceof DocumentExporter) {
      Runnable runnable = new Runnable() {
        @Override
        public void run() {
          addExporter((DocumentExporter)res);
        }
      };
      
      if(SwingUtilities.isEventDispatchThread()) {
        runnable.run();
      } else {
        try {
          SwingUtilities.invokeAndWait(runnable);
        } catch(Exception ex) {
          log.warn("Exception registering document exporter", ex);
        }
      }
    }
  }

  @Override
  public void resourceUnloaded(CreoleEvent e) {
    final Resource res = e.getResource();

    if(res instanceof DocumentExporter) {
      SwingUtilities.invokeLater(new Runnable() {
        
        @Override
        public void run() {
          // TODO Auto-generated method stub
          JMenuItem item = itemByResource.get(res);

          if(item != null) {
            remove(item);
            itemByResource.remove(res);
          }
          
          if(getItemCount() == 2) {
            remove(1);
          }
        }
      });     
    }    
  }

  // remaining CreoleListener methods not used
  @Override
  public void datastoreClosed(CreoleEvent e) {
  }

  @Override
  public void datastoreCreated(CreoleEvent e) {
  }

  @Override
  public void datastoreOpened(CreoleEvent e) {
  }

  @Override
  public void resourceRenamed(Resource resource, String oldName, String newName) {
  }

  private static class DocumentExportDialog extends JDialog {

    private DocumentExporter de;

    private JButton okBtn, fileBtn, cancelBtn;

    private JTextField txtFileName;

    private ResourceParametersEditor parametersEditor;

    private boolean singleFile, userCanceled;
    
    private FeatureMap parameters;

    public DocumentExportDialog() {
      super(MainFrame.getInstance(), "Save As...", true);
      MainFrame.getGuiRoots().add(this);
      initGuiComponents();
      initListeners();
    }

    protected void initGuiComponents() {
      this.getContentPane().setLayout(
              new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));

      // name field
      Box nameBox = Box.createHorizontalBox();
      nameBox.add(Box.createHorizontalStrut(5));
      nameBox.add(new JLabel("Save To:"));
      nameBox.add(Box.createHorizontalStrut(5));
      txtFileName = new JTextField(30);
      txtFileName.setMaximumSize(new Dimension(Integer.MAX_VALUE, txtFileName
              .getPreferredSize().height));
      txtFileName.setRequestFocusEnabled(true);
      txtFileName.setVerifyInputWhenFocusTarget(false);
      nameBox.add(txtFileName);
      // nameField.setToolTipText("Enter a name for the resource");

      nameBox.add(Box.createHorizontalStrut(5));
      nameBox.add(fileBtn = new JButton(MainFrame.getIcon("OpenFile")));
      nameBox.add(Box.createHorizontalGlue());
      this.getContentPane().add(nameBox);
      this.getContentPane().add(Box.createVerticalStrut(5));

      // parameters table
      parametersEditor = new ResourceParametersEditor();
      this.getContentPane().add(new JScrollPane(parametersEditor));
      this.getContentPane().add(Box.createVerticalStrut(5));
      this.getContentPane().add(Box.createVerticalGlue());
      // buttons box
      JPanel buttonsBox = new JPanel();
      buttonsBox.setLayout(new BoxLayout(buttonsBox, BoxLayout.X_AXIS));
      buttonsBox.add(Box.createHorizontalStrut(10));
      buttonsBox.add(okBtn = new JButton("OK"));
      buttonsBox.add(Box.createHorizontalStrut(10));
      buttonsBox.add(cancelBtn = new JButton("Cancel"));
      buttonsBox.add(Box.createHorizontalStrut(10));
      this.getContentPane().add(buttonsBox);
      this.getContentPane().add(Box.createVerticalStrut(5));
      setSize(400, 300);

      getRootPane().setDefaultButton(okBtn);
    }

    protected void initListeners() {
      Action fileAction = new AbstractAction() {

        @Override
        public void actionPerformed(ActionEvent ae) {
          XJFileChooser fileChooser = MainFrame.getFileChooser();
          fileChooser.resetChoosableFileFilters();
          fileChooser.setFileFilter(de.getFileFilter());
          fileChooser.setMultiSelectionEnabled(false);
          fileChooser.setDialogTitle("Save as " + de.getFileType());
          fileChooser.setFileSelectionMode(singleFile
                  ? JFileChooser.FILES_ONLY
                  : JFileChooser.DIRECTORIES_ONLY);

          try {
            File f = new File(txtFileName.getText());
            fileChooser.ensureFileIsVisible(f);
            fileChooser.setSelectedFile(f);
          } catch(Exception e) {
            // swallow and ignore
          }

          if(fileChooser.showSaveDialog(DocumentExportDialog.this) != JFileChooser.APPROVE_OPTION)
            return;

          File selectedFile = fileChooser.getSelectedFile();
          if(selectedFile == null) return;

          txtFileName.setText(selectedFile.getAbsolutePath());
        }
      };

      Action applyAction = new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
          userCanceled = false;
          TableCellEditor cellEditor = parametersEditor.getCellEditor();
          if(cellEditor != null) {
            cellEditor.stopCellEditing();
          }
          setVisible(false);
        }
      };
      Action cancelAction = new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
          userCanceled = true;
          setVisible(false);
        }
      };

      fileBtn.addActionListener(fileAction);
      okBtn.addActionListener(applyAction);
      cancelBtn.addActionListener(cancelAction);

      // disable Enter key in the table so this key will confirm the
      // dialog
      InputMap im =
              parametersEditor
                      .getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
      KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
      im.put(enter, "none");

      // define keystrokes action bindings at the level of the main
      // window
      InputMap inputMap =
              ((JComponent)this.getContentPane())
                      .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
      ActionMap actionMap = ((JComponent)this.getContentPane()).getActionMap();
      inputMap.put(KeyStroke.getKeyStroke("ENTER"), "Apply");
      actionMap.put("Apply", applyAction);
      inputMap.put(KeyStroke.getKeyStroke("ESCAPE"), "Cancel");
      actionMap.put("Cancel", cancelAction);
    }

    public synchronized boolean show(DocumentExporter de,
            List> params, boolean singleFile, String filePath) {

      
      
      this.singleFile = singleFile;
      this.de = de;
      this.parameters = null;

      setTitle("Save as " + de.getFileType());

      txtFileName.setText(filePath);
      parametersEditor.init(null, params);
      pack();
      txtFileName.requestFocusInWindow();
      userCanceled = true;
      setModal(true);
      setLocationRelativeTo(getOwner());
      super.setVisible(true);
      dispose();
      if(userCanceled)
        return false;
      
      //update the feature map to convert values to objects of the correct type.
      
      parameters = parametersEditor.getParameterValues();
      
      for (List disjunction : params) {
        for (Parameter param : disjunction) {
          if (!param.getTypeName().equals("java.lang.String") && parameters.containsKey(param.getName())) {
            Object value = parameters.get(param.getName());
            if (value instanceof String) {
              try {
                parameters.put(param.getName(), param.calculateValueFromString((String)value));
              }
              catch (ParameterException pe) {
                pe.printStackTrace();
                parameters = null;
                return false;
              }
            }
          }
        }
      }
      
      return true;
    }

    @Override
    public void dispose() {
      de = null;
    }

    /**
     * Returns the selected params for the resource or null if none was
     * selected or the user pressed cancel
     */
    public FeatureMap getSelectedParameters() {
      return parameters;
    }

    /**
     * Return the String entered into the resource name field of the
     * dialog.
     * 
     * @param rData
     */
    public String getSelectedFileName() {
      return txtFileName.getText();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy