/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.studio.aciitemeditor.valueeditors;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.ldap.model.subtree.BaseSubtreeSpecification;
import org.apache.directory.api.ldap.model.subtree.SubtreeSpecification;
import org.apache.directory.api.ldap.model.subtree.SubtreeSpecificationParser;
import org.apache.directory.studio.aciitemeditor.Activator;
import org.apache.directory.studio.common.ui.widgets.BaseWidgetUtils;
import org.apache.directory.studio.common.ui.widgets.WidgetModifyEvent;
import org.apache.directory.studio.common.ui.widgets.WidgetModifyListener;
import org.apache.directory.studio.ldapbrowser.common.dialogs.TextDialog;
import org.apache.directory.studio.ldapbrowser.common.widgets.search.EntryWidget;
import org.apache.directory.studio.ldapbrowser.common.widgets.search.FilterWidget;
import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;
/**
* This class provides a dialog to enter the Subtree Specification value.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
class SubtreeSpecificationDialog extends Dialog
{
/** The parser. */
private final SubtreeSpecificationParser parser = new SubtreeSpecificationParser( null );
/** The connection */
private IBrowserConnection connection;
/** The subentry's Dn */
private Dn subentryDn;
/** Flag indicating if the refinement or filter widget should be visible */
private boolean refinementOrFilterVisible;
/** Flag indicating if a local name should be used for the base */
private boolean useLocalName;
/** The initial SubtreeSpecification */
private SubtreeSpecification subtreeSpecification;
/** The Exclusions List */
private List<String> exclusions;
/** The returned SubtreeSpecification */
private String returnValue;
// UI Fields
private EntryWidget entryWidget;
private Spinner minimumSpinner;
private Spinner maximumSpinner;
private TableViewer exclusionsTableViewer;
private Button exclusionsTableEditButton;
private Button exclusionsTableDeleteButton;
private Button refinementButton;
private Text refinementText;
private Button filterButton;
private FilterWidget filterWidget;
/**
* Creates a new instance of SubtreeSpecificationDialog.
*
* @param shell
* the shell to use
* @param connection
* the connection to use
* @param subentryDn
* the subentry's Dn
* @param initialSubtreeSpecification
* the initial SubtreeSpecification
* @param refinementOrFilterVisible
* true if the refinement of filter widget should be visible
* @param useLocalName
* true to use local name for the base
*/
SubtreeSpecificationDialog( Shell shell, IBrowserConnection connection, Dn subentryDn,
String initialSubtreeSpecification, boolean refinementOrFilterVisible, boolean useLocalName )
{
super( shell );
this.connection = connection;
this.subentryDn = subentryDn;
this.refinementOrFilterVisible = refinementOrFilterVisible;
this.useLocalName = useLocalName;
// parse
try
{
subtreeSpecification = parser.parse( initialSubtreeSpecification );
if ( subtreeSpecification == null )
{
subtreeSpecification = new BaseSubtreeSpecification();
}
}
catch ( ParseException pe )
{
// TODO
pe.printStackTrace();
subtreeSpecification = new BaseSubtreeSpecification();
}
exclusions = new ArrayList<String>();
Set<Dn> chopBeforeExclusions = subtreeSpecification.getChopBeforeExclusions();
for ( Dn dn : chopBeforeExclusions )
{
exclusions.add( "chopBefore: \"" + dn.getNormName() + "\"" ); //$NON-NLS-1$ //$NON-NLS-2$
}
Set<Dn> chopAfterExclusions = subtreeSpecification.getChopAfterExclusions();
for ( Dn dn : chopAfterExclusions )
{
exclusions.add( "chopAfter: \"" + dn.getNormName() + "\"" ); //$NON-NLS-1$ //$NON-NLS-2$
}
returnValue = null;
}
/**
* {@inheritDoc}
*/
protected void configureShell( Shell newShell )
{
super.configureShell( newShell );
newShell.setText( Messages.getString( "SubtreeValueEditor.title" ) ); //$NON-NLS-1$
newShell.setImage( Activator.getDefault().getImage( Messages.getString( "SubtreeValueEditor.icon" ) ) ); //$NON-NLS-1$
}
/**
* {@inheritDoc}
*/
protected void okPressed()
{
// set return value
//returnValue = buildSubreeSpecification();
StringBuilder sb = new StringBuilder();
subtreeSpecification.toString( sb );
returnValue = sb.toString();
// save filter and dn history
if ( refinementOrFilterVisible )
{
filterWidget.saveDialogSettings();
}
entryWidget.saveDialogSettings();
super.okPressed();
}
/**
* {@inheritDoc}
*/
protected Control createDialogArea( Composite parent )
{
Composite outer = ( Composite ) super.createDialogArea( parent );
GridData gd = new GridData( GridData.FILL_BOTH );
gd.widthHint = convertHorizontalDLUsToPixels( IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH );
outer.setLayoutData( gd );
Composite composite = BaseWidgetUtils.createColumnContainer( outer, 3, 1 );
BaseWidgetUtils.createLabel( composite, Messages.getString( "SubtreeValueEditor.label.base" ), 1 ); //$NON-NLS-1$
Dn base = subtreeSpecification.getBase();
Dn suffix = null;
if ( subentryDn != null )
{
suffix = subentryDn.getParent();
}
entryWidget = new EntryWidget( connection, base, suffix, useLocalName );
entryWidget.createWidget( composite );
entryWidget.addWidgetModifyListener( new WidgetModifyListener()
{
public void widgetModified( WidgetModifyEvent event )
{
validate();
}
} );
GridData spinnersGridData = new GridData();
spinnersGridData.grabExcessHorizontalSpace = true;
spinnersGridData.verticalAlignment = GridData.CENTER;
spinnersGridData.horizontalSpan = 2;
spinnersGridData.horizontalAlignment = GridData.BEGINNING;
spinnersGridData.widthHint = 3 * 12;
BaseWidgetUtils.createLabel( composite, Messages.getString( "SubtreeValueEditor.label.minimum" ), 1 ); //$NON-NLS-1$
minimumSpinner = new Spinner( composite, SWT.BORDER );
minimumSpinner.setMinimum( 0 );
minimumSpinner.setMaximum( Integer.MAX_VALUE );
minimumSpinner.setDigits( 0 );
minimumSpinner.setIncrement( 1 );
minimumSpinner.setPageIncrement( 100 );
minimumSpinner.setSelection( subtreeSpecification.getMinBaseDistance() );
minimumSpinner.setLayoutData( spinnersGridData );
minimumSpinner.addModifyListener( new ModifyListener()
{
public void modifyText( ModifyEvent event )
{
validate();
}
} );
BaseWidgetUtils.createLabel( composite, Messages.getString( "SubtreeValueEditor.label.maximum" ), 1 ); //$NON-NLS-1$
maximumSpinner = new Spinner( composite, SWT.BORDER );
maximumSpinner.setMinimum( 0 );
maximumSpinner.setMaximum( Integer.MAX_VALUE );
maximumSpinner.setDigits( 0 );
maximumSpinner.setIncrement( 1 );
maximumSpinner.setPageIncrement( 100 );
maximumSpinner.setSelection( subtreeSpecification.getMaxBaseDistance() );
maximumSpinner.setLayoutData( spinnersGridData );
maximumSpinner.addModifyListener( new ModifyListener()
{
public void modifyText( ModifyEvent event )
{
validate();
}
} );
createExclusionsTable( composite );
if ( refinementOrFilterVisible )
{
BaseWidgetUtils.createSpacer( composite, 3 );
createRefinementOrFilterWidgets( composite );
}
applyDialogFont( outer );
validate();
return outer;
}
/**
* Creates the Exclusions Table.
*
* @param composite
* the composite
*/
private void createExclusionsTable( Composite composite )
{
GridData tableGridData = new GridData();
tableGridData.grabExcessHorizontalSpace = true;
tableGridData.verticalAlignment = GridData.FILL;
tableGridData.horizontalAlignment = GridData.FILL;
tableGridData.heightHint = 100;
BaseWidgetUtils.createLabel( composite, Messages.getString( "SubtreeValueEditor.label.exclusions" ), 1 ); //$NON-NLS-1$
Table exclusionsTable = new Table( composite, SWT.BORDER );
exclusionsTable.setHeaderVisible( false );
exclusionsTable.setLayoutData( tableGridData );
exclusionsTable.setLinesVisible( false );
exclusionsTableViewer = new TableViewer( exclusionsTable );
exclusionsTableViewer.setContentProvider( new ArrayContentProvider() );
exclusionsTableViewer.setLabelProvider( new LabelProvider() );
exclusionsTableViewer.setInput( exclusions );
exclusionsTableViewer.addSelectionChangedListener( new ISelectionChangedListener()
{
public void selectionChanged( SelectionChangedEvent event )
{
valueSelectedExclusionsTable();
}
} );
exclusionsTableViewer.addDoubleClickListener( new IDoubleClickListener()
{
public void doubleClick( DoubleClickEvent event )
{
editValueExclusionsTable();
}
} );
GridLayout gridLayout = new GridLayout();
gridLayout.marginWidth = 0;
gridLayout.marginHeight = 0;
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.CENTER;
gridData.grabExcessHorizontalSpace = false;
gridData.grabExcessVerticalSpace = false;
gridData.verticalAlignment = GridData.FILL;
Composite buttonComposite = new Composite( composite, SWT.NONE );
buttonComposite.setLayoutData( gridData );
buttonComposite.setLayout( gridLayout );
GridData buttonGridData = new GridData();
buttonGridData.horizontalAlignment = GridData.FILL;
buttonGridData.grabExcessHorizontalSpace = false;
buttonGridData.verticalAlignment = GridData.BEGINNING;
buttonGridData.widthHint = Activator.getButtonWidth( buttonComposite );
Button exclusionsTableAddButton = new Button( buttonComposite, SWT.PUSH );
exclusionsTableAddButton.setText( Messages.getString( "SubtreeValueEditor.button.add" ) ); //$NON-NLS-1$
exclusionsTableAddButton.setLayoutData( buttonGridData );
exclusionsTableAddButton.addSelectionListener( new SelectionAdapter()
{
public void widgetSelected( SelectionEvent e )
{
addValueExclusionsTable();
}
} );
exclusionsTableEditButton = new Button( buttonComposite, SWT.PUSH );
exclusionsTableEditButton.setText( Messages.getString( "SubtreeValueEditor.button.edit" ) ); //$NON-NLS-1$
exclusionsTableEditButton.setLayoutData( buttonGridData );
exclusionsTableEditButton.addSelectionListener( new SelectionAdapter()
{
public void widgetSelected( SelectionEvent e )
{
editValueExclusionsTable();
}
} );
exclusionsTableEditButton.setEnabled( false );
exclusionsTableDeleteButton = new Button( buttonComposite, SWT.PUSH );
exclusionsTableDeleteButton.setText( Messages.getString( "SubtreeValueEditor.button.delete" ) ); //$NON-NLS-1$
exclusionsTableDeleteButton.setLayoutData( buttonGridData );
exclusionsTableDeleteButton.addSelectionListener( new SelectionAdapter()
{
public void widgetSelected( SelectionEvent e )
{
deleteValueExclusionsTable();
}
} );
exclusionsTableDeleteButton.setEnabled( false );
}
/**
* Creates the refinement or filter widgets
*
* @param composite
* the composite
*/
private void createRefinementOrFilterWidgets( Composite parent )
{
// Messages.getString( "SubtreeValueEditor.label.exclusions" )
BaseWidgetUtils.createLabel( parent, Messages
.getString( "SubtreeValueEditor.SubtreeValueEditor.label.refinementOrFilter" ), 1 ); //$NON-NLS-1$
Composite composite = BaseWidgetUtils.createColumnContainer( parent, 2, 2 );
// refinement redio button
refinementButton = BaseWidgetUtils.createRadiobutton( composite, Messages
.getString( "SubtreeValueEditor.SubtreeValueEditor.label.refinement" ), 2 ); //$NON-NLS-1$
// refinement text
refinementText = new Text( composite, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL );
GridData gd = new GridData( GridData.FILL_BOTH );
gd.horizontalSpan = 2;
gd.widthHint = convertHorizontalDLUsToPixels( IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH );
gd.heightHint = convertHorizontalDLUsToPixels( IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH / 6 );
refinementText.setLayoutData( gd );
try
{
StringBuilder refinementBuffer = new StringBuilder();
if ( subtreeSpecification.getRefinement() != null )
{
subtreeSpecification.getRefinement().printRefinementToBuffer( refinementBuffer );
}
refinementText.setText( refinementBuffer.toString().trim() );
refinementText.setEnabled( true );
refinementButton.setSelection( true );
}
catch ( UnsupportedOperationException e )
{
// thrown if the ExprNode doesn't represent a valid refinement
refinementText.setText( "" ); //$NON-NLS-1$
refinementText.setEnabled( false );
refinementButton.setSelection( false );
}
// filter radio button
filterButton = BaseWidgetUtils.createRadiobutton( composite, Messages
.getString( "SubtreeValueEditor.SubtreeValueEditor.label.filter" ), 2 ); //$NON-NLS-1$
// filter widget
String filter = ""; //$NON-NLS-1$
if ( subtreeSpecification.getRefinement() != null )
{
filter = subtreeSpecification.getRefinement().toString();
}
filterWidget = new FilterWidget( filter );
filterWidget.createWidget( composite );
filterWidget.setBrowserConnection( connection );
filterButton.setSelection( !refinementButton.getSelection() );
filterWidget.setEnabled( !refinementButton.getSelection() );
// add listeners
refinementButton.addSelectionListener( new SelectionAdapter()
{
public void widgetSelected( SelectionEvent arg0 )
{
refinementText.setEnabled( true );
//filterButton.setSelection( false );
filterWidget.setEnabled( false );
validate();
}
} );
refinementText.addModifyListener( new ModifyListener()
{
public void modifyText( ModifyEvent event )
{
validate();
}
} );
filterButton.addSelectionListener( new SelectionAdapter()
{
public void widgetSelected( SelectionEvent arg0 )
{
//refinementButton.setSelection( false );
refinementText.setEnabled( false );
filterWidget.setEnabled( true );
validate();
}
} );
filterWidget.addWidgetModifyListener( new WidgetModifyListener()
{
public void widgetModified( WidgetModifyEvent event )
{
validate();
}
} );
}
/**
* Validates if the composed subtree specification is valid.
*/
private void validate()
{
boolean valid = true;
Dn base = entryWidget.getDn();
valid &= base != null;
String ss = buildSubreeSpecification();
try
{
subtreeSpecification = parser.parse( ss );
valid &= true;
}
catch ( ParseException pe )
{
subtreeSpecification = null;
valid &= false;
}
if ( refinementOrFilterVisible && filterButton.getSelection() )
{
valid &= filterWidget.getFilter() != null;
}
if ( getButton( IDialogConstants.OK_ID ) != null )
{
getButton( IDialogConstants.OK_ID ).setEnabled( valid );
}
}
private String buildSubreeSpecification()
{
// build subtree specification tree
StringBuilder sb = new StringBuilder();
sb.append( "{" ); //$NON-NLS-1$
// Adding base
Dn base = entryWidget.getDn();
if ( ( base != null ) && !SubtreeValueEditor.EMPTY.equals( base.toString() ) )
{
sb.append( " base \"" );
sb.append( base.toString() );
sb.append( "\"," ); //$NON-NLS-1$ //$NON-NLS-2$
}
// Adding Minimum
int minimum = minimumSpinner.getSelection();
if ( minimum != 0 )
{
sb.append( " minimum " ).append( minimum ).append( ',' ); //$NON-NLS-1$ //$NON-NLS-2$
}
// Adding Maximum
int maximum = maximumSpinner.getSelection();
if ( maximum != 0 )
{
sb.append( " maximum " ).append( maximum ).append( ',' ); //$NON-NLS-1$ //$NON-NLS-2$
}
// Adding Exclusions
if ( !exclusions.isEmpty() )
{
sb.append( " specificExclusions {" ); //$NON-NLS-1$
boolean isFirst = true;
for ( String exclusion : exclusions )
{
if ( isFirst )
{
isFirst = false;
}
else
{
sb.append( ',' );
}
sb.append( ' ' ).append( exclusion ); //$NON-NLS-1$
}
sb.append( " }," ); //$NON-NLS-1$
}
// Add Refinement or Filter
String refinementOrFilter = ""; //$NON-NLS-1$
if ( refinementOrFilterVisible )
{
if ( refinementButton.getSelection() )
{
refinementOrFilter = refinementText.getText();
}
else
{
refinementOrFilter = filterWidget.getFilter();
}
}
else
{
refinementOrFilter = ""; //$NON-NLS-1$
}
if ( refinementOrFilter != null && !SubtreeValueEditor.EMPTY.equals( refinementOrFilter ) )
{
sb.append( " specificationFilter " + refinementOrFilter + "," ); //$NON-NLS-1$ //$NON-NLS-2$
}
// Removing the last ','
if ( sb.charAt( sb.length() - 1 ) == ',' )
{
sb.deleteCharAt( sb.length() - 1 );
}
sb.append( " }" ); //$NON-NLS-1$
return sb.toString();
}
/**
* Called when value is selected in Exclusions table viewer.
* Updates the enabled/disabled state of the buttons.
*/
private void valueSelectedExclusionsTable()
{
String value = getSelectedValueExclusionsTable();
if ( value == null )
{
exclusionsTableEditButton.setEnabled( false );
exclusionsTableDeleteButton.setEnabled( false );
}
else
{
exclusionsTableEditButton.setEnabled( true );
exclusionsTableDeleteButton.setEnabled( true );
}
}
/**
* Retuns the current selection in the Exclusions table viewer.
*
* @return
* the value that is selected in the Exclusions table viewer, or null.
*/
private String getSelectedValueExclusionsTable()
{
String value = null;
IStructuredSelection selection = ( IStructuredSelection ) exclusionsTableViewer.getSelection();
if ( !selection.isEmpty() )
{
Object element = selection.getFirstElement();
if ( element instanceof String )
{
value = ( String ) element;
}
}
return value;
}
/**
* Opens the editor and adds the new Exclusion value to the list.
*/
private void addValueExclusionsTable()
{
Dn chopBase = subtreeSpecification.getBase();
if ( useLocalName && ( subentryDn != null ) )
{
Dn suffix = subentryDn.getParent();
if ( !Dn.isNullOrEmpty( suffix ) )
{
try
{
chopBase = chopBase.add( suffix );
}
catch ( LdapInvalidDnException lide )
{
// Do nothing
}
}
}
ExclusionDialog dialog = new ExclusionDialog( getShell(), connection, chopBase, "" ); //$NON-NLS-1$
if ( dialog.open() == TextDialog.OK && !SubtreeValueEditor.EMPTY.equals( dialog.getType() )
&& !SubtreeValueEditor.EMPTY.equals( dialog.getDN() ) )
{
String newValue = dialog.getType() + ": \"" + dialog.getDN() + "\""; //$NON-NLS-1$ //$NON-NLS-2$
exclusions.add( newValue );
exclusionsTableViewer.refresh();
validate();
}
}
/**
* Opens the editor with the currently selected Exclusion
* value and puts the modified value into the list.
*/
private void editValueExclusionsTable()
{
String oldValue = getSelectedValueExclusionsTable();
if ( oldValue != null )
{
Dn chopBase = subtreeSpecification.getBase();
if ( useLocalName && ( subentryDn != null ) )
{
Dn suffix = subentryDn.getParent();
if ( !Dn.isNullOrEmpty( suffix ) )
{
try
{
chopBase = chopBase.add( suffix );
}
catch ( LdapInvalidDnException lide )
{
// Do nothing
}
}
}
ExclusionDialog dialog = new ExclusionDialog( getShell(), connection, chopBase, oldValue );
if ( dialog.open() == TextDialog.OK && !SubtreeValueEditor.EMPTY.equals( dialog.getType() )
&& !SubtreeValueEditor.EMPTY.equals( dialog.getDN() ) )
{
String newValue = dialog.getType() + ": \"" + dialog.getDN() + "\""; //$NON-NLS-1$ //$NON-NLS-2$
exclusions.remove( oldValue );
exclusions.add( newValue );
exclusionsTableViewer.refresh();
validate();
}
}
}
/**
* Deletes the currently selected Exclusion value from list.
*/
private void deleteValueExclusionsTable()
{
String value = getSelectedValueExclusionsTable();
if ( value != null )
{
exclusions.remove( value );
exclusionsTableViewer.refresh();
validate();
}
}
/**
* Gets the subtree specification value or null if canceled.
*
* @return the subtree specification value or null if canceled
*/
public String getSubtreeSpecificationValue()
{
return returnValue;
}
}