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

editor.FlatBeanInfoPopup Maven / Gradle / Ivy

/*
 *
 *  Copyright 2010 Guidewire Software, Inc.
 *
 */
package editor;

import editor.util.EditorUtilities;
import editor.util.TextComponentUtil;
import gw.config.CommonServices;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.IParameterInfo;
import gw.lang.reflect.IPropertyInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeInfo;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.java.IJavaType;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.plaf.basic.BasicGraphicsUtils;
import javax.swing.text.JTextComponent;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------

public class FlatBeanInfoPopup extends JPopupMenu
{
  private static final Map> BEAN_REFERENCE_MAP = new HashMap>();

  private JTree _tree;
  private EventListenerList _nodeListenerList;
  private boolean _bLocked;
  private JTextComponent _editor;
  private IType[] _classes;
  private EditorKeyListener _editorKeyListener;
  private UndoableEditListener _docListener;
  private int _iWidth;

  //--------------------------------------------------------------------------------------------------
  public FlatBeanInfoPopup( IType[] classBeans, JTextComponent editor, boolean bIncludeOnlySimpleDescriptors )
  {
    this( classBeans, editor, bIncludeOnlySimpleDescriptors, true );
  }

  //--------------------------------------------------------------------------------------------------
  public FlatBeanInfoPopup( IType[] classBeans, JTextComponent editor, boolean bIncludeOnlySimpleDescriptors, boolean bBeanGraph )
  {
    super();
    _editor = editor;
    _nodeListenerList = new EventListenerList();
    initLayout( classBeans, bIncludeOnlySimpleDescriptors, bBeanGraph );
  }

  //--------------------------------------------------------------------------------------------------
  protected void initLayout( IType[] classBeans,
                             boolean bIncludeOnlySimpleDescriptors, boolean bBeanGraph )
  {
    _classes = classBeans;

    setLayout( new BorderLayout() );
    setOpaque( false );
    setDoubleBuffered( true );

    JPanel pane = new JPanel();
    GridBagLayout gridBag = new GridBagLayout();
    pane.setLayout( gridBag );
    GridBagConstraints c = new GridBagConstraints();

    int iY = 0;

    BeanTree beanTree;
    if( bBeanGraph )
    {
      //classBeans = getReferences( classBeans );
      Arrays.sort( classBeans,
                   new Comparator()
                   {
                     public int compare( IType o1, IType o2 )
                     {
                       String s1 = o1.getRelativeName();
                       String s2 = o2.getRelativeName();
                       return s1.compareToIgnoreCase( s2 );
                     }
                   } );
    }
    beanTree = new BeanTree( classBeans, null, bIncludeOnlySimpleDescriptors, false );
    _tree = new JTree( new DefaultTreeModel( beanTree ) )
    {
      @Override
      public Dimension getPreferredScrollableViewportSize()
      {
        Dimension dim = super.getPreferredScrollableViewportSize();
        dim.width = _iWidth; // Make the tree a bit roomier.
        return dim;
      }

      @Override
      public boolean isFocusable()
      {
        return false;
      }
    };
    _tree.setRootVisible( false );
    _tree.setShowsRootHandles( true );
    _tree.addMouseListener( new BeanTreeListener() );
    _tree.setCellRenderer( new BeanTreeCellRenderer() );
    _tree.getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION );
    _tree.setSelectionRow( 0 );
    _tree.setVisibleRowCount( 10 );
    _tree.expandRow( 0 );
    JScrollPane scrollPane = new JScrollPane( _tree );
    scrollPane.setBorder( UIManager.getBorder( "TextField.border" ) );

    c.anchor = GridBagConstraints.CENTER;
    c.fill = GridBagConstraints.BOTH;
    c.gridx = 0;
    c.gridy = iY;
    c.gridwidth = GridBagConstraints.REMAINDER;
    c.gridheight = GridBagConstraints.REMAINDER;
    c.weightx = 1;
    c.weighty = 1;
    pane.add( scrollPane, c );

    _editorKeyListener = new EditorKeyListener();
    _docListener = new UndoableEditListener()
    {
      public void undoableEditHappened( UndoableEditEvent e )
      {
        filterDisplay();
      }
    };

    add( BorderLayout.CENTER, pane );
  }

  //--------------------------------------------------------------------------------------------------
  @Override
  public void setVisible( boolean bVisible )
  {
    super.setVisible( bVisible );

    if( bVisible )
    {
      registerListeners();
    }
    else
    {
      unregisterListeners();
      _editor.requestFocus();
    }
  }

  //--------------------------------------------------------------------------------------------------
  void registerListeners()
  {
    unregisterListeners();

    _editor.addKeyListener( _editorKeyListener );
    _editor.getDocument().addUndoableEditListener( _docListener );
  }

  //--------------------------------------------------------------------------------------------------
  void unregisterListeners()
  {
    _editor.getDocument().removeUndoableEditListener( _docListener );
    _editor.removeKeyListener( _editorKeyListener );
  }

  //--------------------------------------------------------------------------------------------------
  IType[] getReferences( IType[] classBeans )
  {
    Set setAggregate = new HashSet();
    for( IType classBean : classBeans )
    {
      Set s = getClassReferenceSet( classBean );
      setAggregate.addAll( s );
    }

    return setAggregate.toArray( new IType[setAggregate.size()] );
  }

  //--------------------------------------------------------------------------------------------------
  void filterDisplay()
  {
//    if( _bShowBeanRoots )
//    {
//      // Only filtering for "code completion" popups.
//      return;
//    }

    String strWholePath = TextComponentUtil.getWordBeforeCaret( _editor );
    String strPrefix = strWholePath;
    if( strWholePath != null && strWholePath.length() > 0 )
    {
      int iDotIndex = strWholePath.lastIndexOf( '.' );
      if( iDotIndex >= 0 )
      {
        strPrefix = strWholePath.substring( iDotIndex + 1 );
      }
    }

    BeanTree beanTree = new BeanTree( _classes[0], null, "", strPrefix, false );

    _tree.setModel( new DefaultTreeModel( beanTree ) );
    _tree.setSelectionRow( 0 );
    _tree.revalidate();
    _tree.repaint();
  }

  //----------------------------------------------------------------------------------------------
  public void show( Component invoker, int iX, int iY, int iWidth )
  {
    _bLocked = true;

    try
    {
      _iWidth = iWidth;
      BeanTree root = (BeanTree)_tree.getModel().getRoot();
      if( root == null || root.getChildCount() == 0 )
      {
        return;
      }

      super.show( invoker, iX, iY );
    }
    finally
    {
      _bLocked = false;
    }
  }

  //----------------------------------------------------------------------------------------------
  public void addNodeChangeListener( ChangeListener l )
  {
    _nodeListenerList.add( ChangeListener.class, l );
  }

  //----------------------------------------------------------------------------------------------
  public void removeNodeChangeListener( ChangeListener l )
  {
    _nodeListenerList.remove( ChangeListener.class, l );
  }

  //--------------------------------------------------------------------------------------------------
  protected void fireNodeChanged( final EventListenerList list, final ChangeEvent e )
  {
    EventQueue.invokeLater( new Runnable()
    {
      public void run()
      {
        fireNodeChangedNow( list, e );
      }
    } );
  }

  //--------------------------------------------------------------------------------------------------
  protected void fireNodeChangedNow( EventListenerList list, ChangeEvent e )
  {
    // Guaranteed to return a non-null array
    Object[] listeners = list.getListenerList();

    // Process the listeners last to first,
    // notifying those that are interested in this event
    for( int i = listeners.length - 2; i >= 0; i -= 2 )
    {
      if( listeners[i] == ChangeListener.class )
      {
        ((ChangeListener)listeners[i + 1]).stateChanged( e );
      }
    }
  }

  static void initBeans()
  {
  }

  //----------------------------------------------------------------------------------------------
  //----------------------------------------------------------------------------------------------
  class EditorKeyListener extends KeyAdapter
  {
    @Override
    public void keyPressed( KeyEvent e )
    {
      if( e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_KP_UP )
      {
        Action selectPrevious = _tree.getActionMap().get( "selectPrevious" );
        selectPrevious.actionPerformed( new ActionEvent( _tree, 0, "selectPrevious" ) );
        e.consume();
      }
      else if( e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_KP_DOWN )
      {
        Action selectNext = _tree.getActionMap().get( "selectNext" );
        selectNext.actionPerformed( new ActionEvent( _tree, 0, "selectNext" ) );
        e.consume();
      }
      else if( e.getKeyCode() == KeyEvent.VK_PAGE_UP )
      {
        Action scrollUpChangeSelection = _tree.getActionMap().get( "scrollUpChangeSelection" );
        scrollUpChangeSelection.actionPerformed( new ActionEvent( _tree, 0, "scrollUpChangeSelection" ) );
        e.consume();
      }
      else if( e.getKeyCode() == KeyEvent.VK_PAGE_DOWN )
      {
        Action scrollDownChangeSelection = _tree.getActionMap().get( "scrollDownChangeSelection" );
        scrollDownChangeSelection.actionPerformed( new ActionEvent( _tree, 0, "scrollDownChangeSelection" ) );
        e.consume();
      }
      else if( e.getKeyCode() == KeyEvent.VK_ENTER ||
               e.getKeyCode() == KeyEvent.VK_SPACE ||
               e.getKeyCode() == KeyEvent.VK_TAB )
      {
        TreePath path = _tree.getSelectionPath();
        if( path == null )
        {
          e.consume();
          return;
        }
        BeanTree tree = (BeanTree)path.getLastPathComponent();

        fireNodeChanged( _nodeListenerList, new ChangeEvent( tree ) );

        setVisible( false );

        e.consume();
      }
      else if( e.getKeyCode() == KeyEvent.VK_ESCAPE )
      {
        setVisible( false );

        e.consume();
      }
    }
  }


  //----------------------------------------------------------------------------------------------
  //----------------------------------------------------------------------------------------------
  class BeanTreeListener extends MouseAdapter
  {
    //----------------------------------------------------------------------------------------------
    @Override
    public void mouseClicked( MouseEvent e )
    {
      if( _bLocked )
      {
        return;
      }

      TreePath path = _tree.getPathForLocation( e.getX(), e.getY() );
      if( path == null )
      {
        return;
      }

      BeanTree tree = (BeanTree)path.getLastPathComponent();

      setVisible( false );
      fireNodeChanged( _nodeListenerList, new ChangeEvent( tree ) );
      e.consume();
    }
  }


  //----------------------------------------------------------------------------------------------
  //----------------------------------------------------------------------------------------------
  class BeanTreeCellRenderer extends JLabel implements TreeCellRenderer
  {
    protected boolean _bSelected;
    protected BeanTree _node;


    //----------------------------------------------------------------------------------------------
    synchronized public Component getTreeCellRendererComponent( JTree tree, Object value,
                                                                boolean bSelected, boolean bExpanded,
                                                                boolean bLeaf, int iRow, boolean bHasFocus )
    {
      if( value != null )
      {
        _node = (BeanTree)value;
        _bSelected = bSelected;
        configure();
      }

      return this;
    }

    //----------------------------------------------------------------------------------------------
    public void update()
    {
      _tree.repaint();
    }

    //----------------------------------------------------------------------------------------------
    public void configure()
    {
      if( _node == null )
      {
        return;
      }

      BeanInfoNode node = _node.getBeanNode();

      ImageIcon icon = null;

      setText( node.getDisplayName() );

      if( node instanceof MethodNode )
      {
        IMethodInfo mi = ((MethodNode)node).getMethodDescriptor();
        if( mi.isPrivate() )
        {
          icon = editor.util.EditorUtilities.loadIcon( "images/Method_Private.png" );
        }
        else if( mi.isInternal() )
        {
          icon = editor.util.EditorUtilities.loadIcon( "images/Method_Sealed.png" );
        }
        else if( mi.isProtected() )
        {
          icon = editor.util.EditorUtilities.loadIcon( "images/Method_Protected.png" );
        }
        else
        {
          icon = editor.util.EditorUtilities.loadIcon( "images/Method.png" );
        }

        if( mi.isDeprecated() )
        {
          setText( "" + getText() + "" );
        }
      }
      else if( node instanceof PropertyNode )
      {
        IPropertyInfo pi = ((PropertyNode)node).getPropertyDescriptor();
        if( pi.isPrivate() )
        {
          icon = editor.util.EditorUtilities.loadIcon( "images/Field_Private.png" );
        }
        else if( pi.isInternal() )
        {
          icon = editor.util.EditorUtilities.loadIcon( "images/Field_Sealed.png" );
        }
        else if( pi.isProtected() )
        {
          icon = editor.util.EditorUtilities.loadIcon( "images/Field_Protected.png" );
        }
        else
        {
          icon = editor.util.EditorUtilities.loadIcon( "images/Field.png" );
        }

        if( pi.isDeprecated() )
        {
          setText( "" + getText() + "" );
        }
      }

      setIcon( icon );
    }

    //----------------------------------------------------------------------------------------------
    @Override
    public void paint( Graphics g )
    {
      Color bkColor;

      if( _bSelected )
      {
        bkColor = _tree.isEnabled() ? EditorUtilities.TEXT_HIGHLIGHT : editor.util.EditorUtilities.CONTROL_SHADOW;
      }
      else
      {
        bkColor = _tree.getBackground();
        if( bkColor == null )
        {
          bkColor = getBackground();
        }
      }

      if( bkColor != null )
      {
        Icon currentIcon = getIcon();

        g.setColor( bkColor );
        if( currentIcon != null && getText() != null )
        {
          int offset = (currentIcon.getIconWidth() + getIconTextGap() - 1);

          g.fillRect( offset, 0, getWidth() - 1 - offset, getHeight() - 1 );

          if( _bSelected && _tree.hasFocus() )
          {
            g.setColor( _tree.isEnabled() ? EditorUtilities.TEXT_HIGHLIGHT_TEXT : editor.util.EditorUtilities.CONTROL_LIGHT );
            BasicGraphicsUtils.drawDashedRect( g, offset, 0, getWidth() - 1 - offset, getHeight() - 1 );
          }

        }
        else
        {
          g.fillRect( 0, 0, getWidth() - 1, getHeight() - 1 );

          if( _bSelected && _tree.hasFocus() )
          {
            g.setColor( _tree.isEnabled() ? EditorUtilities.TEXT_HIGHLIGHT_TEXT : editor.util.EditorUtilities.CONTROL_LIGHT );
            BasicGraphicsUtils.drawDashedRect( g, 0, 0, getWidth() - 1, getHeight() - 1 );
          }
        }
        g.setColor( bkColor );
      }

      setForeground( _bSelected ? _tree.isEnabled() ? EditorUtilities.TEXT_HIGHLIGHT_TEXT
                                                    : editor.util.EditorUtilities.CONTROL_LIGHT
                                : _tree.isEnabled() ? EditorUtilities.TEXT_TEXT
                                                    : editor.util.EditorUtilities.CONTROL_SHADOW );
      super.paint( g );
    }

    //----------------------------------------------------------------------------------------------
    @Override
    public Dimension getPreferredSize()
    {
      Dimension dim = super.getPreferredSize();

      if( dim != null )
      {
        dim = new Dimension( dim.width + 3, dim.height );
      }

      return dim;
    }
  }


  /**
   * From the specified source class builds a set of classes derived from Bean.
   * The source class's reference graph is navigated exhaustively.
   *
   * @param classSource The class of the bean to traverse.
   *
   * @return The set of Bean derived classes referencable from the source class.
   */
  public static Set getClassReferenceSet( IType classSource )
  {
    Set setBeanReferences = BEAN_REFERENCE_MAP.get( classSource );
    if( setBeanReferences == null )
    {
      setBeanReferences = new HashSet();
    }

    getClassReferenceSet( classSource, setBeanReferences, new HashSet() );

    BEAN_REFERENCE_MAP.put( classSource, setBeanReferences );

    return setBeanReferences;
  }

  private static void getClassReferenceSet( IType classSource, Set setBeanReferences, Set setCyclicPrevention )
  {
    try
    {
      if( classSource.isArray() )
      {
        classSource = classSource.getComponentType();
        getClassReferenceSet( classSource, setBeanReferences, setCyclicPrevention );
        return;
      }

      if( setCyclicPrevention.contains( classSource ) )
      {
        // Short circuit cyclic bean info references
        return;
      }
      else
      {
        setCyclicPrevention.add( classSource );
      }


      if( !CommonServices.getEntityAccess().isEntityClass( classSource ) )
      {
        return;
      }

      setBeanReferences.add( classSource );

      //
      // Properties
      //

      ITypeInfo beanInfo = classSource.getTypeInfo();
      if( beanInfo == null )
      {
        return;
      }

      java.util.List properties = TypeSystem.getProperties( beanInfo, classSource );
      for( IPropertyInfo pi : properties )
      {
        if( pi.isHidden() )
        {
          continue;
        }

        getClassReferenceSet( pi.getFeatureType(), setBeanReferences, setCyclicPrevention );
      }

      //
      // Methods
      //

      java.util.List methods = TypeSystem.getMethods( beanInfo, beanInfo.getOwnersType() );
      for( IMethodInfo method : methods )
      {
        if( method.isHidden() )
        {
          continue;
        }

        getClassReferenceSet( method.getReturnType(), setBeanReferences, setCyclicPrevention );
        IParameterInfo[] paramTypes = method.getParameters();
        if( paramTypes != null )
        {
          for( IParameterInfo paramType : paramTypes )
          {
            getClassReferenceSet( paramType.getFeatureType(), setBeanReferences, setCyclicPrevention );
          }
        }
      }

      //
      // Interfaces
      //

      Class[] interfaces = getImplementedInterfaces( classSource );
      for( Class anInterface : interfaces )
      {
        getClassReferenceSet( TypeSystem.get( anInterface ), setBeanReferences, setCyclicPrevention );
      }
    }
    catch( Exception e )
    {
      throw new RuntimeException( e );
    }
  }


  /**
   * Get the complete array of interfaces implemented/extended by the specified
   * class or interface.
   *
   * @param cls The class or interface to inspect.
   *
   * @return An array of all the interfaces the specified class/interface
   * implements/extends.
   */
  public static Class[] getImplementedInterfaces( IType cls )
  {
    if( !(cls instanceof IJavaType) )
    {
      return new Class[0];
    }
    HashSet interfacesSet = new HashSet();
    getImplementedInterfaces( ((IJavaType)cls).getIntrinsicClass(), interfacesSet );
    return interfacesSet.toArray( new Class[interfacesSet.size()] );
  }

  /**
   * Get the complete set of interfaces implemented/extended by the specified
   * class or interface.
   *
   * @param cls           The class or interface to inspect.
   * @param interfacesSet The resulting set of implemented/extended interfaces.
   *
   * @see #getImplementedInterfaces(gw.lang.reflect.IType)
   */
  private static void getImplementedInterfaces( Class cls, Set interfacesSet )
  {
    Class[] interfaces = cls.getInterfaces();
    for( Class anInterface : interfaces )
    {
      if( !interfacesSet.contains( anInterface ) )
      {
        if( !CommonServices.getEntityAccess().isInternal( TypeSystem.get( anInterface ) ) )
        {
          interfacesSet.add( anInterface );
          getImplementedInterfaces( anInterface, interfacesSet );
        }
      }
    }
  }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy