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

gate.swing.ResourceReferenceChooser 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!
package gate.swing;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Window;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTree;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

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

import gate.Gate;
import gate.creole.Plugin;
import gate.creole.Plugin.Maven;
import gate.event.PluginListener;
import gate.resources.img.svg.GATEIcon;
import gate.resources.img.svg.InvalidIcon;
import gate.util.ExtensionFileFilter;

@SuppressWarnings("serial")
public class ResourceReferenceChooser implements PluginListener, TreeWillExpandListener {

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


  private static final int ERROR = -1;

  private static final int CANCEL = 0;

  private static final int APPROVE_FILE = 1;

  private static final int APPROVE_RESOURCE = 2;

  private final InvalidIcon invalidIcon = new InvalidIcon(16, 16);

  private XJFileChooser fileChooser;

  private JOptionPane pluginChooser;

  private JLabel pluginChooserStatus;

  private JComboBox pluginFileFilterBox;

  private JTree pluginTree;

  private DefaultTreeModel pluginTreeModel;

  private DefaultMutableTreeNode treeRoot;

  private JTabbedPane tabPane;

  /** set to true when setSelectedResource is called with a creole: URI */
  private boolean isResourceSelected = false;

  // only valid during a call to showDialog
  private int returnValue = ERROR;

  // only valid during a call to showDialog
  private JDialog dialog;

  public ResourceReferenceChooser() {
    this.fileChooser = new XJFileChooser();

    tabPane = new JTabbedPane();
    
    JPanel panel = new JPanel(new BorderLayout());
    panel.add(fileChooser, BorderLayout.CENTER);
    
    tabPane.addTab("File", panel);
    fileChooser.addActionListener((e) -> {
      if(JFileChooser.APPROVE_SELECTION.equals(e.getActionCommand())) {
        returnValue = APPROVE_FILE;
      } else {
        returnValue = CANCEL;
      }
      if(dialog != null) {
        dialog.setVisible(false);
      }
    });

    tabPane.addHierarchyListener(new HierarchyListener() {
      @Override
      public void hierarchyChanged(HierarchyEvent e) {
        if((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0 && !tabPane.isDisplayable()) {
          // reinitialise fields when the file chooser is hidden
          isResourceSelected = false;
          setSuffixes(null);
        }
      }
    });


    JPanel pluginPanel = new JPanel(new BorderLayout());
    pluginChooserStatus = new JLabel("Select a resource");
    pluginChooserStatus.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    pluginPanel.add(pluginChooserStatus, BorderLayout.NORTH);

    pluginFileFilterBox = new JComboBox<>();
    pluginFileFilterBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    Box fileTypeBox = Box.createHorizontalBox();
    fileTypeBox.add(Box.createHorizontalGlue());
    JLabel ffLabel = new JLabel("File format:");
    ffLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    fileTypeBox.add(ffLabel);
    fileTypeBox.add(pluginFileFilterBox);
    fileTypeBox.add(Box.createHorizontalGlue());
    pluginPanel.add(fileTypeBox, BorderLayout.SOUTH);
    pluginFileFilterBox.addActionListener((e) -> {
      // trick to force the tree to recalculate label widths
      //pluginTree.setRowHeight(pluginTree.getRowHeight());
      pluginTree.repaint();
    });

    treeRoot = new DefaultMutableTreeNode();
    pluginTreeModel = new DefaultTreeModel(treeRoot);
    pluginTree = new JTree(pluginTreeModel);
    pluginTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
    pluginTree.setRootVisible(false);
    pluginTree.setShowsRootHandles(true);
    pluginTree.setCellRenderer(new Renderer());
    JScrollPane treeScroller = new JScrollPane(pluginTree);
    pluginPanel.add(treeScroller, BorderLayout.CENTER);
    pluginChooser = new JOptionPane(pluginPanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
    pluginChooser.addPropertyChangeListener(JOptionPane.VALUE_PROPERTY, (e) -> {
      if(dialog != null && e.getNewValue() instanceof Integer) {
        if(((Integer)e.getNewValue()) == JOptionPane.OK_OPTION) {
          if(pluginTree.getSelectionCount() > 0) {
            try {
              String selected = ((AsUri)pluginTree.getSelectionPath().getLastPathComponent()).getAsURI();
              if(((PluginFileFilter)pluginFileFilterBox.getSelectedItem()).pattern.matcher(selected).find()) {
                returnValue = APPROVE_RESOURCE;
                dialog.setVisible(false);
              } else {
                pluginChooser.setValue(JOptionPane.UNINITIALIZED_VALUE);
                pluginChooserStatus.setIcon(invalidIcon);
                pluginChooserStatus.setText("Selected resource does not have the right suffix");
              }
            } catch(URISyntaxException ex) {
              // shouldn't happen
              pluginChooser.setValue(JOptionPane.UNINITIALIZED_VALUE);
              pluginChooserStatus.setIcon(invalidIcon);
              pluginChooserStatus.setText("Error extracting selected resource from tree");
            }
          } else {
            pluginChooser.setValue(JOptionPane.UNINITIALIZED_VALUE);
            pluginChooserStatus.setIcon(invalidIcon);
            pluginChooserStatus.setText("Nothing selected - please select a resource");
          }
        } else {
          returnValue = CANCEL;
          dialog.setVisible(false);
        }
      }
    });
    tabPane.addTab("Plugin resource", pluginChooser);
    refreshTreeForPlugins();
    pluginTree.expandPath(new TreePath(treeRoot));
    pluginTree.addTreeWillExpandListener(this);

    // handle double click on leaf node as equivalent to clicking OK
    pluginTree.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        if(e.getClickCount() == 2) {
          TreePath selPath = pluginTree.getPathForLocation(e.getX(), e.getY());
          if(selPath != null && ((DefaultMutableTreeNode)selPath.getLastPathComponent()).isLeaf()) {
            pluginChooser.setValue(JOptionPane.OK_OPTION);
          }
        }
      }
    });

    Gate.getCreoleRegister().addPluginListener(this);
    // trigger initial populate of the file type drop downs
    setSuffixes(null);
  }

  @SuppressWarnings("unchecked")
  private void refreshTreeForPlugins() {
    List mavenPluginsWithResources = Gate.getCreoleRegister().getPlugins().stream()
            .filter(p -> (p instanceof Plugin.Maven) && p.hasResources())
            .map(p -> (Plugin.Maven)p)
            .sorted(PLUGIN_COMPARATOR).collect(Collectors.toList());

    // first, remove any nodes for plugins that are no longer loaded
    Enumeration nodes = treeRoot.children();
    Iterator listIt = mavenPluginsWithResources.iterator();
    Plugin.Maven curPlug = listIt.hasNext() ? listIt.next() : null;
    List indicesToRemove = new ArrayList<>(treeRoot.getChildCount());
    List nodesToRemove = new ArrayList<>(treeRoot.getChildCount());
    int i = 0;
    while(nodes.hasMoreElements()) {
      PluginTreeNode n = (PluginTreeNode)nodes.nextElement();
      Plugin.Maven nodePlugin = (Plugin.Maven)n.getUserObject();
      int cmp = -1;
      // advance listIt until we find a curPlug that is >= nodePlugin
      while(curPlug != null && (cmp = PLUGIN_COMPARATOR.compare(nodePlugin, curPlug)) > 0) {
        curPlug = listIt.hasNext() ? listIt.next() : null;
      }
      if(cmp < 0) {
        // we haven't found a plugin in listIt that is == nodePlugin, so node needs to be removed
        indicesToRemove.add(i);
        nodesToRemove.add(n);
      }
      i++;
    }

    if(indicesToRemove.size() > 0) {
      // remove the dead nodes, in reverse order
      ListIterator indicesIt = indicesToRemove.listIterator(indicesToRemove.size());
      while(indicesIt.hasPrevious()) {
        treeRoot.remove(indicesIt.previous());
      }
      // and notify the model
      pluginTreeModel.nodesWereRemoved(treeRoot,
              indicesToRemove.stream().mapToInt(j -> j).toArray(),
              nodesToRemove.toArray());
    }

    // now we need to add new nodes for plugins that have been loaded since last time we checked
    nodes = treeRoot.children();
    listIt = mavenPluginsWithResources.iterator();
    PluginTreeNode curNode = nodes.hasMoreElements() ? (PluginTreeNode)nodes.nextElement() : null;
    List indicesToAdd = new ArrayList<>(mavenPluginsWithResources.size());
    List nodesToAdd = new ArrayList<>(mavenPluginsWithResources.size());
    i = 0;
    while(listIt.hasNext()) {
      Plugin.Maven p = listIt.next();
      int cmp = -1;
      while(curNode != null && (cmp = PLUGIN_COMPARATOR.compare(p, (Plugin.Maven)curNode.getUserObject())) > 0) {
        curNode = nodes.hasMoreElements() ? (PluginTreeNode)nodes.nextElement() : null;
        i++;
      }
      if(cmp < 0 || curNode == null) {
        // we haven't found a node whose plugin is == p, so add a new node for p here
        indicesToAdd.add(i);
        nodesToAdd.add(new PluginTreeNode(p, 0));
        i++;
      }
    }

    if(indicesToAdd.size() > 0) {
      // add the new nodes
      Iterator indicesIt = indicesToAdd.iterator();
      Iterator nodesIt = nodesToAdd.iterator();
      while(indicesIt.hasNext()) {
        treeRoot.insert(nodesIt.next(), indicesIt.next());
      }

      pluginTreeModel.nodesWereInserted(treeRoot, indicesToAdd.stream().mapToInt(j -> j).toArray());
    }

    // now disambiguate cases where there are two plugins with the same name.
    // Keep iterating over the plugin nodes grouping them by their toString().
    // If there are two or more nodes with the same toString() (which initially
    // means plugins with the same name) then increase their nameDepth and try
    // the grouping again.  Eventually we will reach complete disambiguation,
    // in the worst case all the plugins will be listed as full g:a:v triples.
    for(int d = 0; d < 3; d++) {
      Map> groupByDesc = new HashMap<>();
      nodes = treeRoot.children();
      while(nodes.hasMoreElements()) {
        PluginTreeNode n = (PluginTreeNode)nodes.nextElement();
        groupByDesc.computeIfAbsent(n.toString(), (k) -> new ArrayList<>()).add(n);
      }
      boolean anyDups = false;
      for(Map.Entry> entry : groupByDesc.entrySet()) {
        if(entry.getValue().size() > 1) {
          anyDups = true;
          for(PluginTreeNode groupNode : entry.getValue()) {
            groupNode.nameDepth++;
          }
        }
      }
    }
  }

  public void setResource(String resource) {
    fileChooser.setResource(resource);
  }

  public String getResource() {
    return fileChooser.getResource();
  }

  @SuppressWarnings("unchecked")
  public void setSelectedResource(String uriStr) {
    try {
      URI uri = new URI(uriStr);
      if("creole".equals(uri.getScheme())) {
        String path = uri.getPath();
        URI pluginBaseUri = uri.resolve("/");
        // find the plugin node matching this base URI
        PluginTreeNode pluginNode = null;
        Enumeration pluginNodes = treeRoot.children();
        while(pluginNodes.hasMoreElements()) {
          PluginTreeNode n = (PluginTreeNode)pluginNodes.nextElement();
          URI bu = ((Plugin.Maven) n.getUserObject()).getBaseURI();
          if(bu.equals(pluginBaseUri)) {
            pluginNode = n;
            break;
          }
        }

        if(pluginNode != null) {
          // this expand will trigger an enumerate of the plugin resources if necessary
          pluginTree.expandPath(new TreePath(new Object[] {treeRoot, pluginNode}));
          DefaultMutableTreeNode nearestNode = pluginNode;
          LinkedList nodesToTry = new LinkedList<>();
          Enumeration children = pluginNode.children();
          while(children.hasMoreElements()) {
            nodesToTry.add((PluginResourceTreeNode)children.nextElement());
          }
          ListIterator iter = nodesToTry.listIterator();
          while(iter.hasNext()) {
            PluginResourceTreeNode n = iter.next();
            // if we might be able to go deeper here, add this node's children
            // after the current iterator cursor position
            if(path.startsWith(n.getFullPath())) {
              nearestNode = n;
              int stepsBack = 0;
              children = n.children();
              while(children.hasMoreElements()) {
                iter.add((PluginResourceTreeNode)children.nextElement());
                stepsBack++;
              }
              for(int i = 0; i < stepsBack; i++) {
                iter.previous();
              }
            }
          }

          // nearestNode is now either the entry itself or its nearest ancestor if not present
          // (e.g. if the URI is to /foo/bar/baz.txt and there isn't one of those but there is
          // a /foo/bar/ then nearestNode will point to that).  In the worst case it'll be the
          // root of the same plugin.

          TreeNode[] pathToRoot = nearestNode.getPath();
          // collapse siblings at each level except the very top
          for(int i = 1; i < pathToRoot.length; i++) {
            DefaultMutableTreeNode n = (DefaultMutableTreeNode)pathToRoot[i];
            Enumeration siblings = n.getParent().children();
            while(siblings.hasMoreElements()) {
              DefaultMutableTreeNode sib = (DefaultMutableTreeNode)siblings.nextElement();
              if(sib != n) {
                pluginTree.collapsePath(new TreePath(sib.getPath()));
              }
            }
          }

          // expand parent
          pluginTree.expandPath(new TreePath(Arrays.copyOf(pathToRoot, pathToRoot.length-1)));

          // and select me
          TreePath selPath = new TreePath(pathToRoot);
          pluginTree.setSelectionPath(selPath);
          pluginTree.scrollPathToVisible(selPath);

          // and make sure the plugin tree tab is selected
          tabPane.setSelectedIndex(1);
          isResourceSelected = true;
        }
      } else if("file".equals(uri.getScheme())) {
        fileChooser.setSelectedFile(new File(uri));
        tabPane.setSelectedIndex(0);
      }
    } catch(URISyntaxException e) {
      // oh well, we tried
    }
  }

  public void setSuffixes(Collection suffixes) {
    setSuffixes((suffixes == null ? null : "Known file types " + suffixes.toString()), suffixes);
  }

  public void setSuffixes(String description, Collection suffixes) {
    fileChooser.resetChoosableFileFilters();
    fileChooser.setAcceptAllFileFilterUsed(true);
    fileChooser.setFileFilter(fileChooser.getAcceptAllFileFilter());

    pluginFileFilterBox.removeAllItems();
    PluginFileFilter allFilter = new PluginFileFilter();
    allFilter.pattern = Pattern.compile("");
    allFilter.description = "All files";
    pluginFileFilterBox.addItem(allFilter);
    pluginFileFilterBox.setSelectedItem(allFilter);

    if(suffixes != null && !suffixes.isEmpty()) {
      ExtensionFileFilter fileFilter = new ExtensionFileFilter();
      suffixes.forEach((suf) -> fileFilter.addExtension(suf));
      fileFilter.setDescription(description);
      fileChooser.addChoosableFileFilter(fileFilter);
      fileChooser.setFileFilter(fileFilter);

      PluginFileFilter suffixFilter = new PluginFileFilter();
      suffixFilter.description = description;
      suffixFilter.pattern = Pattern.compile("(?:" +
              suffixes.stream().map(Pattern::quote).collect(Collectors.joining("|"))
              + ")$");
      pluginFileFilterBox.addItem(suffixFilter);
      pluginFileFilterBox.setSelectedItem(suffixFilter);
    }
  }

  @SuppressWarnings("unchecked")
  public String showDialog(Window parent, String title) {
    if(dialog != null) {
      throw new IllegalStateException("ResourceReferenceChooser dialog already showing");
    }
    fileChooser.setSelectedFileFromPreferences();
    if(!isResourceSelected) {
      // nothing was selected, so prefer file chooser tab
      tabPane.setSelectedIndex(0);

      String resourceClass = getResource();
      if(resourceClass != null) {
        // attempt to find a plugin that defines this resource
        Enumeration nodes = treeRoot.children();
        while(nodes.hasMoreElements()) {
          PluginTreeNode node = (PluginTreeNode)nodes.nextElement();
          Plugin.Maven plugin = (Plugin.Maven)node.getUserObject();
          if(plugin.getResourceInfoList().stream().anyMatch(ri -> resourceClass.equals(ri.getResourceClassName()))) {
            TreePath path = new TreePath(node.getPath());
            pluginTree.setSelectionPath(path);
            pluginTree.expandPath(path);
            pluginTree.scrollPathToVisible(path);
            break;
          } else {
            // collapse everything else
            Enumeration descendants = node.postorderEnumeration();
            while(descendants.hasMoreElements()) {
              pluginTree.collapsePath(new TreePath(((DefaultMutableTreeNode)descendants.nextElement()).getPath()));
            }
          }
        }
      }
    }

    dialog = new JDialog(parent, title, Dialog.DEFAULT_MODALITY_TYPE);
    try {
      pluginChooser.setValue(JOptionPane.UNINITIALIZED_VALUE);
      pluginChooserStatus.setIcon(null);
      pluginChooserStatus.setText("Select a resource");
      dialog.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
      dialog.addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent e) {
          returnValue = CANCEL;
        }
      });
      returnValue = ERROR;
      dialog.getContentPane().setLayout(new BorderLayout());
      dialog.getContentPane().add(tabPane, BorderLayout.CENTER);
      dialog.pack();
      dialog.setLocationRelativeTo(parent);
      dialog.setVisible(true); // blocks until window closed

      if(returnValue == APPROVE_RESOURCE) {
        TreePath selected = pluginTree.getSelectionPath();
        if(selected== null) {
          return null;
        } else {
          return ((AsUri) selected.getLastPathComponent()).getAsURI();
        }
      } else if(returnValue == APPROVE_FILE) {
        return fileChooser.getSelectedFile().toURI().toString();
      } else {
        // cancel
        return null;
      }
    } catch(URISyntaxException ex) {
      // shouldn't happen
      throw new RuntimeException(ex);
    } finally {
      dialog.getContentPane().removeAll();
      dialog.dispose();
      dialog = null;
    }
  }



  private static Comparator PLUGIN_COMPARATOR =
          Comparator.comparing(Maven::getName)
                  .thenComparing(Maven::getGroup)
                  .thenComparing(Maven::getArtifact)
                  .thenComparing(Maven::getVersion);

  @Override
  public void pluginLoaded(Plugin plugin) {
    SwingUtilities.invokeLater(this::refreshTreeForPlugins);
  }

  @Override
  public void pluginUnloaded(Plugin plugin) {
    SwingUtilities.invokeLater(this::refreshTreeForPlugins);
  }

  @Override
  public void treeWillExpand(TreeExpansionEvent event) {
    DefaultMutableTreeNode expandingNode = (DefaultMutableTreeNode)event.getPath().getLastPathComponent();
    if(expandingNode instanceof PluginTreeNode) {
      try {
        ((PluginTreeNode)expandingNode).expanding();
      } catch(IOException | URISyntaxException e) {
        log.warn("Error processing resources for plugin " + expandingNode.getUserObject(), e);
      }
    }
  }

  @Override
  public void treeWillCollapse(TreeExpansionEvent event) {
  }

  static interface AsUri {
    String getAsURI() throws URISyntaxException;
  }
  static class PluginTreeNode extends DefaultMutableTreeNode implements AsUri {
    int nameDepth = 0;
    boolean hasExpanded = false;

    public PluginTreeNode(Plugin.Maven plugin, int nameDepth) {
      super(plugin, true);
      this.nameDepth = nameDepth;
    }

    public String getAsURI() throws URISyntaxException {
      return ((Plugin)getUserObject()).getBaseURI().toString();
    }

    public String toString() {
      Plugin.Maven p = (Plugin.Maven)getUserObject();
      if(nameDepth == 0) {
        return p.getName();
      } else if(nameDepth == 1) {
        return p.getName() + " (" + p.getVersion() + ")";
      } else if(nameDepth == 2) {
        return p.getName() + " (" + p.getArtifact() + ":" + p.getVersion() + ")";
      } else {
        return p.getName() + " (" + p.getGroup() + ":" + p.getArtifact() + ":" + p.getVersion() + ")";
      }
    }

    public boolean isLeaf() {
      return false;
    }

    void expanding() throws IOException, URISyntaxException {
      if(hasExpanded) return;
      hasExpanded = true;

      final Plugin.Maven thisPlugin = (Plugin.Maven)getUserObject();
      final LinkedList nodeStack = new LinkedList<>();
      final LinkedList> childLists = new LinkedList<>();
      nodeStack.addLast(this);
      childLists.addLast(new ArrayList<>());
      thisPlugin.walkResources(new SimpleFileVisitor() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
          if(dir.getNameCount() > 1) { // no node for the resources dir itself
            String sep = dir.getFileSystem().getSeparator();
            String name = dir.getFileName().toString();
            if(name.endsWith(sep)) {
              name = name.substring(0, name.length() - sep.length());
            }
            PluginResourceTreeNode dirNode = new PluginResourceTreeNode(thisPlugin, dir.toString(), name, true);
            childLists.getLast().add(dirNode); // add this dir to parent list
            childLists.addLast(new ArrayList<>()); // and start a new list for us
            nodeStack.addLast(dirNode);
          }
          return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
          PluginResourceTreeNode fileNode = new PluginResourceTreeNode(thisPlugin, file.toString(), file.getFileName().toString(), false);
          childLists.getLast().add(fileNode);
          return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
          List children = childLists.removeLast();
          DefaultMutableTreeNode thisDirNode = nodeStack.removeLast();
          Collections.sort(children);
          for(DefaultMutableTreeNode node : children) {
            thisDirNode.add(node);
          }
          return FileVisitResult.CONTINUE;
        }
      });
    }
  }

  static class PluginResourceTreeNode extends DefaultMutableTreeNode implements Comparable, AsUri {
    private Plugin.Maven plugin;
    private String fullPath;
    private boolean directory;

    PluginResourceTreeNode(Plugin.Maven plugin, String fullPath, String myName, boolean directory) {
      super(myName);
      this.directory = directory;
      this.plugin = plugin;
      this.fullPath = fullPath;
    }

    public String getFullPath() {
      return fullPath;
    }

    public String getAsURI() throws URISyntaxException {
      URI pluginBase = plugin.getBaseURI();
      return new URI(pluginBase.getScheme(), pluginBase.getAuthority(), fullPath, null, null).toString();
    }

    @Override
    public boolean isLeaf() {
      return !directory;
    }

    @Override
    public int compareTo(PluginResourceTreeNode other) {
      return ((String)getUserObject()).compareToIgnoreCase((String)other.getUserObject());
    }
  }

  class Renderer extends DefaultTreeCellRenderer {
    private Icon pluginIcon;

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
      JLabel renderer = (JLabel)super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
      if(value instanceof PluginTreeNode) {
        renderer.setEnabled(true);
        renderer.setIcon(pluginIcon);
      } else if(value instanceof PluginResourceTreeNode) {
        PluginResourceTreeNode node = (PluginResourceTreeNode)value;
        if(!node.isLeaf() || ((PluginFileFilter)pluginFileFilterBox.getSelectedItem()).pattern.matcher(node.fullPath).find()) {
          renderer.setEnabled(true);
        } else {
          // trick lifted from DefaultTreeCellRenderer to get the right disabled icon
          Icon icon = renderer.getIcon();
          LookAndFeel laf = UIManager.getLookAndFeel();
          Icon disabledIcon = laf.getDisabledIcon(tree, icon);
          if (disabledIcon != null) icon = disabledIcon;
          renderer.setDisabledIcon(icon);
          renderer.setEnabled(false);
        }
      }
      return renderer;
    }

    @Override
    public void updateUI() {
      super.updateUI();
      Icon defaultIcon = getDefaultClosedIcon();
      // create plugin icon at the same size as the default closed folder icon from the L&F
      pluginIcon =
          new GATEIcon(defaultIcon == null ? 16 : defaultIcon.getIconWidth(),
              defaultIcon == null ? 16 : defaultIcon.getIconHeight());
    }
  }

  static class PluginFileFilter {
    Pattern pattern;

    String description;

    public String toString() {
      return description;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy