Package org.exist.client.xacml

Source Code of org.exist.client.xacml.TreeMutator

package org.exist.client.xacml;

import java.awt.Cursor;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.net.URI;

import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import com.sun.xacml.Policy;
import com.sun.xacml.PolicySet;
import com.sun.xacml.PolicyTreeElement;
import com.sun.xacml.Rule;

public class TreeMutator implements ActionListener, DragGestureListener, DragSourceListener, DropTargetListener, KeyListener, MouseListener, PopupMenuListener
{
  private static final String NEW_RULE = "New Rule";
  private static final String NEW_POLICY = "New Policy";
  private static final String NEW_POLICY_SET = "New Policy Set";
  private static final String REMOVE = "Remove"

  public static final int BIAS_BEFORE = -1;
  public static final int BIAS_CURRENT = 0;
  public static final int BIAS_AFTER = 1;
  public static final int BIAS_NO_DESTINATION = -2;
 
  private static final int BIAS_DELTA_Y = 4;

  private XACMLTreeNode currentDestinationNode = null;
  private int destinationBias = 0;
 
  private NodeCopyAction copyAction;
  private NodeExpander expander;
  private AutoScroller scroller;
 
  private JTree tree;
  private JPopupMenu popup;
  private XACMLTreeNode contextNode;
 
  @SuppressWarnings("unused")
  private TreeMutator() {}
  public TreeMutator(JTree tree)
  {
    if(tree == null)
      {throw new NullPointerException("Tree cannot be null");}
    popup = new JPopupMenu();
   
    this.tree = tree;
   
    scroller = new AutoScroller();
    expander = new NodeExpander(tree);
    copyAction = new NodeCopyAction(tree);
    tree.getInputMap().put(copyAction.getTrigger(), copyAction.getName());
    tree.getActionMap().put(copyAction.getName(), copyAction);
   
    tree.setDragEnabled(false);
    tree.setTransferHandler(null);
    tree.addMouseListener(this);
    tree.addKeyListener(this);
    DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(tree, DnDConstants.ACTION_COPY_OR_MOVE, this);
    new DropTarget(tree, this);

    reset();
  }
  public JTree getTree()
  {
    return tree;
  }
 
  public void reset()
  {
    contextNode = null;
    copyAction.setContextNode(null);
    popup.removeAll();
    if(popup.isVisible())
      {popup.setVisible(false);}
  }

  //MouseListener methods
  public void mouseClicked(MouseEvent event)
  {
    showPopup(event);
  }
  public void mouseEntered(MouseEvent event)
  {
    showPopup(event);
  }
  public void mouseExited(MouseEvent event)
  {
    showPopup(event);
  }
  public void mousePressed(MouseEvent event)
  {
    showPopup(event);
  }
  public void mouseReleased(MouseEvent event)
  {
    showPopup(event);
  }
  private void showPopup(MouseEvent event)
  { 
    if(!popup.isPopupTrigger(event))
      {return;}
    reset();
    final Object source = event.getSource();
    if(source != tree)
      {return;}
   
    final Point p = event.getPoint();
    final int row = tree.getClosestRowForLocation(p.x, p.y);
    if(row == -1)
    {
      showRootPopup(p);
      return;
    }
   
    final Rectangle bounds = tree.getRowBounds(row);
    if(bounds.y > p.y || bounds.y + bounds.height <= p.y)
    {
      showRootPopup(p);
      return;
    }
   
    final TreePath path = tree.getPathForRow(row);
    if(path == null)
    {
      showRootPopup(p);
      return;
    }
   
   
    final Object last = path.getLastPathComponent();
    final XACMLTreeNode node = (XACMLTreeNode)last;
    copyAction.setContextNode(node);
   
    if(!(last instanceof PolicyElementNode))
    {
      popup.add(copyAction);
      popup.show(tree, p.x, p.y);
      return;
    }
   
    contextNode = (XACMLTreeNode)last;
    handleTreeElementNode();
   
    popup.addSeparator();
    popup.add(copyAction);
   
    popup.show(tree, p.x, p.y);
  }
  private void handleTreeElementNode()
  {
    if(contextNode instanceof PolicySetNode)
    {
      addPolicySetItem();
      addPolicyItem();
    }
    else if(contextNode instanceof PolicyNode)
      {addRuleItem();}
    //else if(contextNode instanceof Rule)
    //  do nothing in this case
   
    addRemoveItem();
  }
  private void addRemoveItem()
  {
    final JMenuItem remove = new JMenuItem(REMOVE, KeyEvent.VK_R);
    remove.addActionListener(this);
    popup.add(remove);
  }
  private void addRuleItem()
  {
    final JMenuItem newRule = new JMenuItem(NEW_RULE, KeyEvent.VK_R);
    newRule.addActionListener(this);
    popup.add(newRule);
  }
  private void addPolicyItem()
  {
    final JMenuItem newPolicy = new JMenuItem(NEW_POLICY, KeyEvent.VK_P);
    newPolicy.addActionListener(this);
    popup.add(newPolicy);
  }
  private void addPolicySetItem()
  {
    final JMenuItem newPolicySet = new JMenuItem(NEW_POLICY_SET, KeyEvent.VK_S);
    newPolicySet.addActionListener(this);
    popup.add(newPolicySet);
  }

  private void showRootPopup(Point p)
  {
    contextNode = getRootNode();
   
    addPolicySetItem();
    addPolicyItem();
   
    popup.show(tree, p.x, p.y);
  }
  private RootNode getRootNode()
  {
    final TreeModel model = tree.getModel();
    if(!(model instanceof XACMLTreeModel))
      {return null;}
    final XACMLTreeModel xmodel = (XACMLTreeModel)model;
    return (RootNode)xmodel.getRoot();
  }
 
  private void newRule()
  {
    if(contextNode instanceof PolicyNode)
    {
      final PolicyNode node = (PolicyNode)contextNode;
      final Rule rule = XACMLEditor.createDefaultRule(node);
      node.add(rule);
    }
  }
  private void newPolicySet()
  {
    if(contextNode instanceof PolicySetNode || contextNode instanceof RootNode)
    {
      final PolicyElementContainer node =((PolicyElementContainer)contextNode);
      final PolicySet ps = XACMLEditor.createDefaultPolicySet(node);
      node.add(ps);
    }
  }
  private void newPolicy()
  {
    if(contextNode instanceof PolicySetNode || contextNode instanceof RootNode)
    {
      final PolicyElementContainer node =((PolicyElementContainer)contextNode);
      final Policy p = XACMLEditor.createDefaultPolicy(node);
      node.add(p);
    }
  }
  private void remove()
  {
    if(contextNode == null)
      {return;}
    final NodeContainer parent = contextNode.getParent();
    if(parent instanceof PolicyElementContainer && contextNode instanceof PolicyElementNode)
      {((PolicyElementContainer)parent).remove((PolicyElementNode)contextNode);}
  }

  public void actionPerformed(ActionEvent event)
  {
    final String actionCommand = event.getActionCommand();
    if(actionCommand == null)
      {return;}
    else if(actionCommand.equals(NEW_RULE))
      {newRule();}
    else if(actionCommand.equals(NEW_POLICY))
      {newPolicy();}
    else if(actionCommand.equals(NEW_POLICY_SET))
      {newPolicySet();}
    else if(actionCommand.equals(REMOVE))
      {remove();}
    tree.revalidate();
    tree.repaint();
  }
 


  //KeyListener methods
  public void keyPressed(KeyEvent event)
  {
    //avoid collisions with JTree's builtin bindings
    if(event.isShiftDown() || event.isControlDown() || !event.isAltDown())
      {return;}
   
    final int keyCode = event.getKeyCode();
    int delta;
    if(keyCode == KeyEvent.VK_UP)
      {delta = -1;}
    else if(keyCode == KeyEvent.VK_DOWN)
      {delta = 1;}
    else
      {return;}
    final TreePath selected = tree.getSelectionPath();
    if(selected == null)
      {return;}
    final XACMLTreeNode treeNode = (XACMLTreeNode)selected.getLastPathComponent();
    if(!(treeNode instanceof PolicyElementNode))
      {return;}
    final PolicyElementNode node = (PolicyElementNode)treeNode;
    final PolicyElementContainer parent = (PolicyElementContainer)node.getParent();
    int currentIndex = parent.indexOfChild(node);
    if(currentIndex < 0)
      {return;}
    currentIndex += delta;
    if(currentIndex < 0 || currentIndex >= parent.getChildCount())
      {return;}
    if(currentIndex == 0 && !(parent instanceof RootNode))
      {return;}
    tree.clearSelection();
    parent.remove(node);
    parent.add(currentIndex, node);
    tree.setSelectionPath(XACMLTreeModel.getPathToNode(node));
  }

  public void keyReleased(KeyEvent event) {}
  public void keyTyped(KeyEvent event) {}
 
  //PopupMenuListener methods
  public void popupMenuCanceled(PopupMenuEvent arg0) {}
  public void popupMenuWillBecomeInvisible(PopupMenuEvent event)
  {
    reset();
  }
  public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) {}

 
  //  DragGestureListener method
  public void dragGestureRecognized(DragGestureEvent event)
  {
    final Point location = event.getDragOrigin();
    final TreePath path = tree.getPathForLocation(location.x, location.y);
    if(path == null)
      {return;}

    final int action = event.getDragAction();
    final XACMLTreeNode transferNode = (XACMLTreeNode)path.getLastPathComponent();
    final Cursor cursor = (action == DnDConstants.ACTION_MOVE) ? DragSource.DefaultMoveDrop : DragSource.DefaultCopyDrop;
    event.startDrag(cursor, new NodeTransferable(transferNode), this);
  }
  // DropTargetListener methods
  public void drop(DropTargetDropEvent event)
  {
    event.acceptDrop(event.getDropAction());
   
    boolean success = false;
    try
    {
      success = handleDrop(event);
    }
    //these exceptions should not happen:
    //  the flavor is checked and the returned
    //  data requires no IO
    catch(final IOException ioe)
    {
      success = false;
    }
    catch(final UnsupportedFlavorException ufe)
    {
      success = false;
    }
    finally
    {
      haltTimers();
      clearDestination();
      event.dropComplete(success);
    }
  }
  public void dragOver(DropTargetDragEvent event)
  {
    checkDrag(event);
  }
  public void dragEnter(DropTargetDragEvent event)
  {
    checkDrag(event);
  }
  public void dropActionChanged(DropTargetDragEvent event)
  {
    checkDrag(event);
  }
  public void dragExit(DropTargetEvent event)
  {
    haltTimers();
    clearDestination();
  }
  private void haltTimers()
  {
    scroller.stop();
    expander.stop();
  }

  private void checkDrag(DropTargetDragEvent event)
  {
    final XACMLTreeNode oldNode = currentDestinationNode;
    final int oldBias = destinationBias;
   
    final Point location = event.getLocation();
    updateCurrentDestination(location, event.getDropAction());
   
    if(currentDestinationNode == null)
    {
      expander.stop();
      clearDestination();
      event.rejectDrag();
      return;
    }

    scroller.autoscroll(tree, location);
   
    if(destinationBias != BIAS_CURRENT)
      {expander.stop();}
    else if(oldNode != currentDestinationNode || destinationBias != oldBias)
      {expander.hover(currentDestinationNode);}
   
    if(supportsDrop(event))
      {repaintDestination(oldNode, oldBias);}
    else
      {clearDestination();}
  }

  private boolean supportsDrop(DropTargetDragEvent event)
  {
    int action = event.getDropAction();
    boolean supported;
    updateCurrentDestination(event.getLocation(), action);
    if(currentDestinationNode == null)
      {supported = false;}
    else if(event.isDataFlavorSupported(NodeTransferable.TARGET_FLAVOR))
    {
      if(action == DnDConstants.ACTION_COPY_OR_MOVE || action == DnDConstants.ACTION_MOVE)
        {action = DnDConstants.ACTION_COPY;}
      supported = isTargetDropValid(action);
    }
    else if(event.isDataFlavorSupported(NodeTransferable.CONDITION_FLAVOR))
    {
      if(action == DnDConstants.ACTION_COPY_OR_MOVE || action == DnDConstants.ACTION_MOVE)
        {action = DnDConstants.ACTION_COPY;}
      supported = isConditionDropValid(action);
    }
    else if(event.isDataFlavorSupported(NodeTransferable.RULE_FLAVOR))
      {supported = isRuleDropValid(action);}
    else if(event.isDataFlavorSupported(NodeTransferable.ABSTRACT_POLICY_FLAVOR))
      {supported = isAbstractPolicyDropValid(action);}
    else
      {supported = false;}
   
    if(supported)
      {event.acceptDrag(action);}
    else
      {event.rejectDrag();}
    return supported;
  }
 
  private boolean isTargetDropValid(int action)
  {
    if(action == DnDConstants.ACTION_MOVE)
      {return false;}
    if(currentDestinationNode instanceof PolicyElementNode || currentDestinationNode instanceof TargetNode)
      {return destinationBias == TreeMutator.BIAS_CURRENT;}
    return false;
  }
  private boolean isConditionDropValid(int action)
  {
    if(action == DnDConstants.ACTION_MOVE)
      {return false;}
    if(currentDestinationNode instanceof ConditionNode)
      {return destinationBias == TreeMutator.BIAS_CURRENT;}
    if(currentDestinationNode instanceof RuleNode || currentDestinationNode instanceof ConditionNode)
      {return destinationBias == TreeMutator.BIAS_CURRENT;}
    return false;
  }
  private boolean isRuleDropValid(int action)
  {
    if(currentDestinationNode instanceof PolicyNode)
      {return destinationBias == TreeMutator.BIAS_CURRENT;}
    if(currentDestinationNode instanceof RuleNode)
      {return destinationBias == TreeMutator.BIAS_AFTER || destinationBias == TreeMutator.BIAS_BEFORE;}
    if(currentDestinationNode instanceof TargetNode && currentDestinationNode.getParent() instanceof PolicyNode)
      {return destinationBias == TreeMutator.BIAS_AFTER;}
    return false;
  }
  private boolean isAbstractPolicyDropValid(int action)
  {
    if(currentDestinationNode instanceof PolicySetNode || currentDestinationNode instanceof RootNode)
      {return true;}
    if(currentDestinationNode instanceof PolicyNode)
      {return destinationBias == TreeMutator.BIAS_AFTER || destinationBias == TreeMutator.BIAS_BEFORE;}
    if(currentDestinationNode instanceof TargetNode && currentDestinationNode.getParent() instanceof PolicySetNode)
      {return destinationBias == TreeMutator.BIAS_AFTER;}
    return false;
  }
  private boolean isPolicyElementDropValid(int action, PolicyElementNode srcNode)
  {
    if(srcNode instanceof RuleNode)
      {return isRuleDropValid(action);}
    else if(srcNode instanceof AbstractPolicyNode)
      {return isAbstractPolicyDropValid(action);}
    else
      {return false;}
  }

  private boolean handleDrop(DropTargetDropEvent event) throws IOException, UnsupportedFlavorException
  {
    final Transferable data = event.getTransferable();
    final int action = event.getDropAction();
   
    updateCurrentDestination(event.getLocation(), event.getDropAction());
    if(currentDestinationNode == null)
      {return false;}
   
    if(data.isDataFlavorSupported(NodeTransferable.TARGET_FLAVOR))
    {
      if(!isTargetDropValid(action))
        {return false;}
      TargetNode destTarget;
      if(currentDestinationNode instanceof PolicyElementNode)
        {destTarget = ((PolicyElementNode)currentDestinationNode).getTarget();}
      else if(currentDestinationNode instanceof TargetNode)
        {destTarget = (TargetNode)currentDestinationNode;}
      else
        {return false;}
      final TargetNode source = (TargetNode)data.getTransferData(NodeTransferable.TARGET_FLAVOR);
      destTarget.setTarget(source.getTarget());
      return true;
    }
    if(data.isDataFlavorSupported(NodeTransferable.CONDITION_FLAVOR))
    {
      if(!isConditionDropValid(action))
        {return false;}
      ConditionNode destCondition;
      if(currentDestinationNode instanceof RuleNode)
        {destCondition = ((RuleNode)currentDestinationNode).getCondition();}
      else if(currentDestinationNode instanceof ConditionNode)
        {destCondition = (ConditionNode)currentDestinationNode;}
      else
        {return false;}
      final ConditionNode source = (ConditionNode)data.getTransferData(NodeTransferable.CONDITION_FLAVOR);
      destCondition.setCondition(source.getCondition());
      return true;
    }
    if(data.isDataFlavorSupported(NodeTransferable.POLICY_ELEMENT_FLAVOR))
    {
      final PolicyElementNode srcNode = (PolicyElementNode)data.getTransferData(NodeTransferable.POLICY_ELEMENT_FLAVOR);
      final PolicyElementContainer oldParent = (PolicyElementContainer)srcNode.getParent();

      if(!isPolicyElementDropValid(action, srcNode))
        {return false;}
     
     
      PolicyElementContainer newParent;
      if(destinationBias == TreeMutator.BIAS_CURRENT)
        {newParent = (PolicyElementContainer)currentDestinationNode;}
      else
        {newParent = (PolicyElementContainer)currentDestinationNode.getParent();}
           
      if(isDescendantOrSelf(srcNode, newParent))
        {return false;}
     
      if(action == DnDConstants.ACTION_MOVE)
      {
        if(oldParent != null)
          {oldParent.remove(srcNode);}
      }
     
      int insertionIndex = newParent.indexOfChild(currentDestinationNode);
      if(insertionIndex < 0)
        {insertionIndex = newParent.getChildCount();}
      else if(destinationBias == TreeMutator.BIAS_AFTER)
        {insertionIndex++;}
     
      if(action == DnDConstants.ACTION_MOVE && oldParent == newParent)
        {newParent.add(insertionIndex, srcNode);}
      else
      {
        PolicyTreeElement copy;
        final String currentId = srcNode.getId().toString();
        if(newParent.containsId(currentId))
          {copy = srcNode.create(URI.create(XACMLEditor.createUniqueId(newParent, currentId)));}
        else
          {copy = srcNode.create();}
        newParent.add(insertionIndex, copy);
      }
      return true;
    }
    return false;
  }

  private boolean isDescendantOrSelf(PolicyElementNode srcNode, PolicyElementContainer newParent)
  {
    final TreePath srcPath = XACMLTreeModel.getPathToNode(srcNode);
    final TreePath newParentPath = XACMLTreeModel.getPathToNode(newParent);
    if(srcPath == null || newParentPath == null)
      {return false;}
    return srcNode == newParent || srcPath.isDescendant(newParentPath);
  }
  private void updateCurrentDestination(Point location, int dropAction)
  {
    final TreePath currentPath = tree.getClosestPathForLocation(location.x, location.y);
    if(currentPath == null)
    {
      currentDestinationNode = null;
      destinationBias = BIAS_NO_DESTINATION;
      return;
    }
    currentDestinationNode = (XACMLTreeNode)currentPath.getLastPathComponent();
   
    final int row = tree.getRowForPath(currentPath);
    final Rectangle bounds = tree.getRowBounds(row);
    if(bounds.y > location.y || location.y < BIAS_DELTA_Y)
      {destinationBias = BIAS_BEFORE;}
    else if(bounds.y + bounds.height <= location.y)
      {destinationBias = BIAS_AFTER;}
    else
    {
      if(isDestinationDifferent(tree.getClosestPathForLocation(location.x, location.y - BIAS_DELTA_Y)))
        {destinationBias = BIAS_BEFORE;}
      else if(isDestinationDifferent(tree.getClosestPathForLocation(location.x, location.y + BIAS_DELTA_Y)))
        {destinationBias = BIAS_AFTER;}
      else
        {destinationBias = BIAS_CURRENT;}
    }
  }
  private boolean isDestinationDifferent(TreePath path)
  {
    return (path == null) ? (currentDestinationNode != null) : (currentDestinationNode != path.getLastPathComponent());
  }
  public int getDestinationBias(XACMLTreeNode testNode)
  {
    return (currentDestinationNode == null || destinationBias == BIAS_NO_DESTINATION || currentDestinationNode != testNode) ? BIAS_NO_DESTINATION : destinationBias;
  }
  private void clearDestination()
  {
    final XACMLTreeNode oldNode = currentDestinationNode;
    final int oldBias = destinationBias;
    currentDestinationNode = null;
    destinationBias = BIAS_NO_DESTINATION;
    repaintDestination(oldNode, oldBias);
  }
  private void repaintDestination(XACMLTreeNode oldNode, int oldBias)
  {
    if(oldNode != null && oldBias != BIAS_NO_DESTINATION)
      {handleRepaintDestination(oldNode, oldBias);}
    if(currentDestinationNode != null && destinationBias != BIAS_NO_DESTINATION)
      {handleRepaintDestination(currentDestinationNode, destinationBias);}
  }
  private void handleRepaintDestination(XACMLTreeNode node, int bias)
  {
    if(node == null || bias == BIAS_NO_DESTINATION)
      {return;}

    final int row = tree.getRowForPath(XACMLTreeModel.getPathToNode(node));
    repaintRow(row);
    if(bias == BIAS_AFTER)
    {
      if(row+1 < tree.getRowCount())
        {repaintRow(row+1);}
    }
    else if(bias == BIAS_BEFORE)
    {
      if(row > 0)
        {repaintRow(row - 1);}
    }
  }
  private void repaintRow(int row)
  {
    final Rectangle rect = tree.getRowBounds(row);
    if (rect != null)
      {tree.repaint(rect);}
  }


  //DragSourceListener methods
  public void dropActionChanged(DragSourceDragEvent event) { }
  public void dragEnter(DragSourceDragEvent event) {}
  public void dragOver(DragSourceDragEvent event) {}
  public void dragDropEnd(DragSourceDropEvent event) {}
  public void dragExit(DragSourceEvent event) {}
}
TOP

Related Classes of org.exist.client.xacml.TreeMutator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.