001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.xdbm.tools;
021    
022    
023    import java.awt.Color;
024    import java.awt.Font;
025    import java.awt.Frame;
026    import java.awt.Panel;
027    import java.awt.event.ActionEvent;
028    import java.awt.event.ActionListener;
029    
030    import javax.swing.BorderFactory;
031    import javax.swing.BoxLayout;
032    import javax.swing.ButtonGroup;
033    import javax.swing.JButton;
034    import javax.swing.JDialog;
035    import javax.swing.JLabel;
036    import javax.swing.JOptionPane;
037    import javax.swing.JPanel;
038    import javax.swing.JRadioButton;
039    import javax.swing.JScrollPane;
040    import javax.swing.JTabbedPane;
041    import javax.swing.JTable;
042    import javax.swing.JTextArea;
043    import javax.swing.JTextField;
044    import javax.swing.border.TitledBorder;
045    import javax.swing.table.DefaultTableModel;
046    
047    import org.apache.directory.server.xdbm.Index;
048    import org.apache.directory.server.xdbm.IndexEntry;
049    import org.apache.directory.server.xdbm.ForwardIndexEntry;
050    import org.apache.directory.server.i18n.I18n;
051    import org.apache.directory.shared.ldap.cursor.Cursor;
052    import org.apache.directory.shared.ldap.entry.ServerEntry;
053    import org.apache.directory.shared.ldap.util.ExceptionUtils;
054    import org.apache.directory.shared.ldap.NotImplementedException;
055    
056    import org.slf4j.Logger;
057    import org.slf4j.LoggerFactory;
058    
059    
060    /**
061     * A dialog showing index values.
062     *
063     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
064     * @version $Rev: 639279 $
065     */
066    public class IndexDialog<K, O, ID> extends JDialog
067    {
068        private static final Logger LOG = LoggerFactory.getLogger( IndexDialog.class );
069    
070        private static final long serialVersionUID = 3689917253680445238L;
071    
072        public static final String DEFAULT_CURSOR = "Default";
073        public static final String EQUALITY_CURSOR = "Equality";
074        public static final String GREATER_CURSOR = "Greater";
075        public static final String LESS_CURSOR = "Less";
076        public static final String REGEX_CURSOR = "Regex";
077    
078        private Panel mainPnl = new Panel();
079        private JTabbedPane tabbedPane = new JTabbedPane();
080        private JPanel listPnl = new JPanel();
081        private JPanel cursorPnl = new JPanel();
082        private JPanel resultsPnl = new JPanel();
083        private JScrollPane jScrollPane2 = new JScrollPane();
084        private JTable resultsTbl = new JTable();
085        private JPanel buttonPnl = new JPanel();
086        private JButton doneBut = new JButton();
087        private JLabel jLabel1 = new JLabel();
088        private JTextField keyText = new JTextField();
089        private JLabel jLabel2 = new JLabel();
090        private JButton scanBut = new JButton();
091    
092        private Index<K, O, ID> index = null;
093    
094    
095        public IndexDialog( Frame parent, boolean modal, Index<K, O, ID> index )
096        {
097            super( parent, modal );
098            this.index = index;
099            initGUI();
100        }
101    
102    
103        public IndexDialog( Index<K, O, ID> index )
104        {
105            super();
106            this.index = index;
107            initGUI();
108        }
109    
110    
111        /**
112         * This method is called from within the constructor to initialize the
113         * form.
114         */
115        private void initGUI()
116        {
117            addWindowListener( new java.awt.event.WindowAdapter()
118            {
119                public void windowClosing( java.awt.event.WindowEvent evt )
120                {
121                    closeDialog();
122                }
123            } );
124    
125            pack();
126            setTitle( "Index On Attribute '" + index.getAttribute().getName() + "'" );
127            setBounds( new java.awt.Rectangle( 0, 0, 512, 471 ) );
128            getContentPane().add( mainPnl, java.awt.BorderLayout.CENTER );
129            mainPnl.setLayout( new java.awt.BorderLayout() );
130            mainPnl.add( tabbedPane, java.awt.BorderLayout.CENTER );
131            tabbedPane.add( listPnl, "Listing" );
132            listPnl.setLayout( new java.awt.GridBagLayout() );
133    
134            RadioButtonListener radioListener = new RadioButtonListener();
135            JRadioButton radioDefault = new JRadioButton( DEFAULT_CURSOR );
136            radioDefault.setActionCommand( DEFAULT_CURSOR );
137            radioDefault.setSelected( true );
138            radioDefault.addActionListener( radioListener );
139    
140            JRadioButton radioEquality = new JRadioButton( EQUALITY_CURSOR );
141            radioEquality.setActionCommand( EQUALITY_CURSOR );
142            radioEquality.addActionListener( radioListener );
143    
144            JRadioButton radioGreater = new JRadioButton( GREATER_CURSOR );
145            radioGreater.setActionCommand( GREATER_CURSOR );
146            radioGreater.addActionListener( radioListener );
147    
148            JRadioButton radioLess = new JRadioButton( LESS_CURSOR );
149            radioLess.setActionCommand( LESS_CURSOR );
150            radioLess.addActionListener( radioListener );
151    
152            JRadioButton radioRegex = new JRadioButton( REGEX_CURSOR );
153            radioRegex.setActionCommand( REGEX_CURSOR );
154            radioRegex.addActionListener( radioListener );
155    
156            ButtonGroup group = new ButtonGroup();
157            group.add( radioDefault );
158            group.add( radioEquality );
159            group.add( radioGreater );
160            group.add( radioLess );
161            group.add( radioRegex );
162    
163            JPanel radioPanel = new JPanel();
164            radioPanel.setLayout( new BoxLayout( radioPanel, BoxLayout.X_AXIS ) );
165            radioPanel.add( radioDefault );
166            radioPanel.add( radioEquality );
167            radioPanel.add( radioGreater );
168            radioPanel.add( radioLess );
169            radioPanel.add( radioRegex );
170    
171            listPnl.add( cursorPnl, new java.awt.GridBagConstraints( 0, 0, 1, 1, 1.0, 0.15,
172                java.awt.GridBagConstraints.NORTH, java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 15, 0, 30, 0 ),
173                0, 0 ) );
174            listPnl.add( resultsPnl, new java.awt.GridBagConstraints( 0, 1, 1, 1, 1.0, 0.8,
175                java.awt.GridBagConstraints.CENTER, java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 0, 0, 0, 0 ), 0,
176                0 ) );
177            listPnl.add( buttonPnl, new java.awt.GridBagConstraints( 0, 2, 1, 1, 1.0, 0.05,
178                java.awt.GridBagConstraints.CENTER, java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 0, 0, 0, 0 ), 0,
179                0 ) );
180            cursorPnl.setLayout( new java.awt.GridBagLayout() );
181            cursorPnl.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
182                new java.awt.Color( 153, 153, 153 ), 1 ), "Display Cursor Constraints",
183                javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, new java.awt.Font(
184                    "SansSerif", 0, 14 ), new java.awt.Color( 60, 60, 60 ) ) );
185            cursorPnl.add( jLabel1, new java.awt.GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0,
186                java.awt.GridBagConstraints.WEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 0, 15, 0, 10 ), 0,
187                0 ) );
188            cursorPnl.add( keyText, new java.awt.GridBagConstraints( 1, 1, 1, 1, 0.4, 0.0,
189                java.awt.GridBagConstraints.WEST, java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 5, 5, 5, 236 ), 0,
190                0 ) );
191            cursorPnl.add( jLabel2, new java.awt.GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0,
192                java.awt.GridBagConstraints.WEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 0, 15, 0, 10 ), 0,
193                0 ) );
194            cursorPnl.add( radioPanel,
195                new java.awt.GridBagConstraints( 1, 0, 1, 1, 0.4, 0.0, java.awt.GridBagConstraints.WEST,
196                    java.awt.GridBagConstraints.NONE, new java.awt.Insets( 5, 5, 5, 0 ), 0, 0 ) );
197            resultsPnl.setLayout( new java.awt.BorderLayout() );
198            resultsPnl.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
199                new java.awt.Color( 153, 153, 153 ), 1 ), "Scan Results", javax.swing.border.TitledBorder.LEADING,
200                javax.swing.border.TitledBorder.TOP, new java.awt.Font( "SansSerif", 0, 14 ), new java.awt.Color( 60, 60,
201                    60 ) ) );
202            resultsPnl.add( jScrollPane2, java.awt.BorderLayout.CENTER );
203            jScrollPane2.getViewport().add( resultsTbl );
204            buttonPnl.setLayout( new java.awt.FlowLayout( java.awt.FlowLayout.CENTER, 15, 5 ) );
205            buttonPnl.add( doneBut );
206            buttonPnl.add( scanBut );
207            doneBut.setText( "Done" );
208            doneBut.addActionListener( new ActionListener()
209            {
210                public void actionPerformed( ActionEvent e )
211                {
212                    closeDialog();
213                }
214            } );
215    
216            jLabel1.setText( "Key Constraint:" );
217            keyText.setText( "" );
218            keyText.setMinimumSize( new java.awt.Dimension( 130, 20 ) );
219            keyText.setPreferredSize( new java.awt.Dimension( 130, 20 ) );
220            keyText.setMaximumSize( new java.awt.Dimension( 130, 20 ) );
221            keyText.setFont( new java.awt.Font( "SansSerif", java.awt.Font.PLAIN, 14 ) );
222            keyText.setSize( new java.awt.Dimension( 130, 20 ) );
223            jLabel2.setText( "Cursor Type:" );
224    
225            scanBut.setText( "Scan" );
226            scanBut.addActionListener( new ActionListener()
227            {
228                @SuppressWarnings("unchecked")
229                public void actionPerformed( ActionEvent e )
230                {
231                    //noinspection unchecked
232                    doScan( ( K ) keyText.getText(), selectedCursorType );
233                }
234            } );
235    
236            doScan( null, DEFAULT_CURSOR );
237        }
238    
239        private String selectedCursorType = DEFAULT_CURSOR;
240    
241        class RadioButtonListener implements ActionListener
242        {
243            public void actionPerformed( ActionEvent e )
244            {
245                if ( e.getActionCommand().equals( DEFAULT_CURSOR ) )
246                {
247                    selectedCursorType = DEFAULT_CURSOR;
248                }
249                else if ( e.getActionCommand().equals( EQUALITY_CURSOR ) )
250                {
251                    selectedCursorType = EQUALITY_CURSOR;
252                }
253                else if ( e.getActionCommand().equals( GREATER_CURSOR ) )
254                {
255                    selectedCursorType = GREATER_CURSOR;
256                }
257                else if ( e.getActionCommand().equals( LESS_CURSOR ) )
258                {
259                    selectedCursorType = LESS_CURSOR;
260                }
261                else if ( e.getActionCommand().equals( REGEX_CURSOR ) )
262                {
263                    selectedCursorType = REGEX_CURSOR;
264                }
265            }
266        }
267    
268    
269        private void closeDialog()
270        {
271            setVisible( false );
272            dispose();
273        }
274    
275    
276        public boolean doScan( K key, String scanType )
277        {
278            if ( key == null && !scanType.equals( DEFAULT_CURSOR ) )
279            {
280                JOptionPane.showMessageDialog( null, "Cannot use a " + scanType + " scan type with a null key constraint.",
281                    "Missing Key Constraint", JOptionPane.ERROR_MESSAGE );
282                return false;
283            }
284    
285            Object[] cols = new Object[2];
286            Object[] row;
287            cols[0] = "Keys ( Attribute Value )";
288            cols[1] = "Values ( Entry Id )";
289            DefaultTableModel model = new DefaultTableModel( cols, 0 );
290            int count = 0;
291    
292            try
293            {
294                Cursor<IndexEntry<K, O, ID>> list;
295    
296                if ( scanType.equals( EQUALITY_CURSOR ) )
297                {
298                    list = index.forwardCursor( key );
299                    list.beforeFirst();
300                    while ( list.next() )
301                    {
302                        IndexEntry<K, O, ID> rec = list.get();
303                        row = new Object[2];
304                        row[0] = rec.getValue();
305                        row[1] = rec.getId();
306                        model.addRow( row );
307                        count++;
308                    }
309                }
310                else if ( scanType.equals( GREATER_CURSOR ) )
311                {
312                    list = index.forwardCursor();
313                    ForwardIndexEntry<K, O, ID> entry = new ForwardIndexEntry<K, O, ID>();
314                    entry.setValue( key );
315                    list.before( entry );
316                    while ( list.next() )
317                    {
318                        IndexEntry<K, O, ID> rec = list.get();
319                        row = new Object[2];
320                        row[0] = rec.getValue();
321                        row[1] = rec.getId();
322                        model.addRow( row );
323                        count++;
324                    }
325                }
326                else if ( scanType.equals( LESS_CURSOR ) )
327                {
328                    list = index.forwardCursor();
329                    ForwardIndexEntry<K, O, ID> entry = new ForwardIndexEntry<K, O, ID>();
330                    entry.setValue( key );
331                    list.after( entry );
332                    while ( list.previous() )
333                    {
334                        IndexEntry<K, O, ID> rec = list.get();
335                        row = new Object[2];
336                        row[0] = rec.getValue();
337                        row[1] = rec.getId();
338                        model.addRow( row );
339                        count++;
340                    }
341                }
342                else if ( scanType.equals( REGEX_CURSOR ) )
343                {
344                    //                Pattern regex = StringTools.getRegex( key );
345                    //                int starIndex = key.indexOf( '*' );
346                    //
347                    //                if ( starIndex > 0 )
348                    //                {
349                    //                    String prefix = key.substring( 0, starIndex );
350                    //
351                    //                    if ( log.isDebugEnabled() )
352                    //                        log.debug( "Regex prefix = " + prefix );
353                    //
354                    //                    list = index.listIndices( regex, prefix );
355                    //                }
356                    //                else
357                    //                {
358                    //                    list = index.listIndices( regex );
359                    //                }
360                    throw new NotImplementedException();
361                }
362                else
363                {
364                    list = index.forwardCursor();
365                    while ( list.next() )
366                    {
367                        IndexEntry<K, O, ID> rec = list.get();
368                        row = new Object[2];
369                        row[0] = rec.getValue();
370                        row[1] = rec.getId();
371                        model.addRow( row );
372                        count++;
373                    }
374                }
375    
376                resultsTbl.setModel( model );
377                resultsPnl.setBorder( BorderFactory.createTitledBorder( BorderFactory.createLineBorder( new Color( 153,
378                    153, 153 ), 1 ), "Scan Results: " + count, TitledBorder.LEADING, TitledBorder.TOP, new Font(
379                    "SansSerif", 0, 14 ), new Color( 60, 60, 60 ) ) );
380    
381                if ( isVisible() )
382                {
383                    validate();
384                }
385            }
386            catch ( Exception e )
387            {
388                String msg = ExceptionUtils.getStackTrace( e );
389    
390                if ( msg.length() > 1024 )
391                {
392                    msg = msg.substring( 0, 1024 ) + "\n. . . TRUNCATED . . .";
393                }
394    
395                msg = I18n.err( I18n.ERR_183, index.getAttribute(), scanType, key, msg );
396    
397                LOG.error( msg, e );
398                JTextArea area = new JTextArea();
399                area.setText( msg );
400                JOptionPane.showMessageDialog( null, area, "Index Scan Error", JOptionPane.ERROR_MESSAGE );
401                return false;
402            }
403    
404            return true;
405        }
406    
407    
408        @SuppressWarnings("unchecked")
409        public static void show( Index<?, ServerEntry, Long> index )
410        {
411            IndexDialog<?, ServerEntry, Long> dialog = new IndexDialog( index );
412            dialog.setVisible( true );
413        }
414    }