/* * 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.ldapbrowser.common.wizards; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.apache.directory.api.ldap.model.constants.SchemaConstants; import org.apache.directory.api.ldap.model.schema.ObjectClass; import org.apache.directory.api.ldap.model.schema.ObjectClassTypeEnum; import org.apache.directory.api.util.Strings; import org.apache.directory.studio.common.ui.widgets.BaseWidgetUtils; import org.apache.directory.studio.connection.ui.RunnableContextRunner; import org.apache.directory.studio.ldapbrowser.common.BrowserCommonActivator; import org.apache.directory.studio.ldapbrowser.common.BrowserCommonConstants; import org.apache.directory.studio.ldapbrowser.core.events.EventRegistry; import org.apache.directory.studio.ldapbrowser.core.jobs.ReloadSchemaRunnable; import org.apache.directory.studio.ldapbrowser.core.model.IAttribute; import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection; import org.apache.directory.studio.ldapbrowser.core.model.IValue; import org.apache.directory.studio.ldapbrowser.core.model.impl.Attribute; import org.apache.directory.studio.ldapbrowser.core.model.impl.DummyEntry; import org.apache.directory.studio.ldapbrowser.core.model.impl.Value; import org.apache.directory.studio.ldapbrowser.core.model.schema.Schema; import org.apache.directory.studio.ldapbrowser.core.model.schema.SchemaUtils; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.fieldassist.FieldDecorationRegistry; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; 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.graphics.Image; 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.Label; import org.eclipse.swt.widgets.Text; /** * The NewEntryTypeWizardPage is used to select the entry's object classes. * * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> */ public class NewEntryObjectclassWizardPage extends WizardPage { /** The Constant SIZING_SELECTION_WIDGET_HEIGHT. */ private final static int SIZING_SELECTION_WIDGET_HEIGHT = 250; /** The Constant SIZING_SELECTION_WIDGET_WIDTH. */ private final static int SIZING_SELECTION_WIDGET_WIDTH = 400; /** The wizard. */ private NewEntryWizard wizard; /** The available object classes. */ private List<ObjectClass> availableObjectClasses; /** The available object classes instant search. */ private Text availableObjectClassesInstantSearch; /** The available object classes viewer. */ private TableViewer availableObjectClassesViewer; /** The selected object classes. */ private List<ObjectClass> selectedObjectClasses; /** The selected object classes viewer. */ private TableViewer selectedObjectClassesViewer; /** The add button. */ private Button addButton; /** The remove button. */ private Button removeButton; private LabelProvider labelProvider = new LabelProvider() { /** * {@inheritDoc} */ public String getText( Object element ) { if ( element instanceof ObjectClass ) { ObjectClass ocd = ( ObjectClass ) element; return SchemaUtils.toString( ocd ); } // Default return super.getText( element ); } /** * {@inheritDoc} */ public Image getImage( Object element ) { if ( element instanceof ObjectClass ) { ObjectClass ocd = ( ObjectClass ) element; switch ( ocd.getType() ) { case STRUCTURAL: return BrowserCommonActivator.getDefault().getImage( BrowserCommonConstants.IMG_OCD_STRUCTURAL ); case ABSTRACT: return BrowserCommonActivator.getDefault().getImage( BrowserCommonConstants.IMG_OCD_ABSTRACT ); case AUXILIARY: return BrowserCommonActivator.getDefault().getImage( BrowserCommonConstants.IMG_OCD_AUXILIARY ); default: return BrowserCommonActivator.getDefault().getImage( BrowserCommonConstants.IMG_OCD ); } } // Default return super.getImage( element ); } }; /** * Creates a new instance of NewEntryObjectclassWizardPage. * * @param pageName the page name * @param wizard the wizard */ public NewEntryObjectclassWizardPage( String pageName, NewEntryWizard wizard ) { super( pageName ); setTitle( Messages.getString( "NewEntryObjectclassWizardPage.ObjectClasses" ) ); //$NON-NLS-1$ setDescription( Messages.getString( "NewEntryObjectclassWizardPage.ObjectClassesDescription" ) ); //$NON-NLS-1$ setImageDescriptor( BrowserCommonActivator.getDefault().getImageDescriptor( BrowserCommonConstants.IMG_ENTRY_WIZARD ) ); setPageComplete( false ); this.wizard = wizard; this.availableObjectClasses = new ArrayList<ObjectClass>(); this.selectedObjectClasses = new ArrayList<ObjectClass>(); } /** * Validates the input fields. */ private void validate() { if ( !selectedObjectClasses.isEmpty() ) { boolean hasOneStructuralOC = false; for ( ObjectClass ocd : selectedObjectClasses ) { if ( ocd.getType() == ObjectClassTypeEnum.STRUCTURAL ) { hasOneStructuralOC = true; break; } } if ( !hasOneStructuralOC ) { setMessage( Messages.getString( "NewEntryObjectclassWizardPage.SelectStructuralObjectClass" ), WizardPage.WARNING ); //$NON-NLS-1$ } else { setMessage( null ); } setPageComplete( true ); saveState(); } else { setPageComplete( false ); setMessage( null ); } } /** * Loads the state of selected and available object classes from * the prototype entry. Called when this page becomes visible. */ private void loadState() { availableObjectClasses.clear(); selectedObjectClasses.clear(); if ( wizard.getSelectedConnection() != null ) { availableObjectClasses.addAll( wizard.getSelectedConnection().getSchema().getObjectClassDescriptions() ); DummyEntry newEntry = wizard.getPrototypeEntry(); IAttribute ocAttribute = newEntry.getAttribute( SchemaConstants.OBJECT_CLASS_AT ); if ( ocAttribute != null ) { for ( IValue ocValue : ocAttribute.getValues() ) { if ( !ocValue.isEmpty() ) { ObjectClass ocd = wizard.getSelectedConnection().getSchema() .getObjectClassDescription( ocValue.getStringValue() ); availableObjectClasses.remove( ocd ); selectedObjectClasses.add( ocd ); } } } } availableObjectClassesViewer.refresh(); selectedObjectClassesViewer.refresh(); } /** * Saves the state of selected object classes to the entry. */ private void saveState() { DummyEntry newEntry = wizard.getPrototypeEntry(); try { EventRegistry.suspendEventFiringInCurrentThread(); // set new objectClass values IAttribute ocAttribute = newEntry.getAttribute( SchemaConstants.OBJECT_CLASS_AT ); if ( ocAttribute == null ) { ocAttribute = new Attribute( newEntry, SchemaConstants.OBJECT_CLASS_AT ); newEntry.addAttribute( ocAttribute ); } IValue[] values = ocAttribute.getValues(); for ( IValue value : values ) { ocAttribute.deleteValue( value ); } for ( ObjectClass ocd : selectedObjectClasses ) { ocAttribute.addValue( new Value( ocAttribute, ocd.getNames().get( 0 ) ) ); } } finally { EventRegistry.resumeEventFiringInCurrentThread(); } } /** * {@inheritDoc} * * This implementation initializes the list of available and selected * object classes when this page becomes visible. */ public void setVisible( boolean visible ) { super.setVisible( visible ); if ( visible ) { loadState(); validate(); availableObjectClassesInstantSearch.setFocus(); } } /** * {@inheritDoc} */ public void createControl( Composite parent ) { Composite composite = new Composite( parent, SWT.NONE ); GridLayout gl = new GridLayout( 3, false ); composite.setLayout( gl ); composite.setLayoutData( new GridData( GridData.FILL_BOTH ) ); Label availableLabel = new Label( composite, SWT.NONE ); availableLabel.setText( Messages.getString( "NewEntryObjectclassWizardPage.AvailableObjectClasses" ) ); //$NON-NLS-1$ Label buttonLabel = new Label( composite, SWT.NONE ); buttonLabel.setText( "" ); //$NON-NLS-1$ Label selectedLabel = new Label( composite, SWT.NONE ); selectedLabel.setText( Messages.getString( "NewEntryObjectclassWizardPage.SelectedObjectClasses" ) ); //$NON-NLS-1$ Composite availableObjectClassesComposite = BaseWidgetUtils.createColumnContainer( composite, 1, 1 ); Composite availableObjectClassesInstantSearchComposite = BaseWidgetUtils.createColumnContainer( availableObjectClassesComposite, 2, 1 ); availableObjectClassesInstantSearch = new Text( availableObjectClassesInstantSearchComposite, SWT.NONE | SWT.BORDER | SWT.SEARCH | SWT.CANCEL ); availableObjectClassesInstantSearch.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) ); availableObjectClassesInstantSearch.addModifyListener( new ModifyListener() { public void modifyText( ModifyEvent e ) { availableObjectClassesViewer.refresh(); if ( availableObjectClassesViewer.getTable().getItemCount() >= 1 ) { Object item = availableObjectClassesViewer.getElementAt( 0 ); availableObjectClassesViewer.setSelection( new StructuredSelection( item ) ); } } } ); availableObjectClassesInstantSearch.addKeyListener( new KeyAdapter() { public void keyPressed( KeyEvent e ) { if ( e.keyCode == SWT.ARROW_DOWN ) { availableObjectClassesViewer.getTable().setFocus(); } else if ( e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR ) { add( availableObjectClassesViewer.getSelection() ); } } } ); ControlDecoration availableObjectClassesInstantSearchDecoration = new ControlDecoration( availableObjectClassesInstantSearch, SWT.TOP | SWT.LEFT, composite ); availableObjectClassesInstantSearchDecoration.setDescriptionText( Messages .getString( "NewEntryObjectclassWizardPage.FilterDescription" ) ); //$NON-NLS-1$ availableObjectClassesInstantSearchDecoration.setImage( FieldDecorationRegistry.getDefault() .getFieldDecoration( FieldDecorationRegistry.DEC_CONTENT_PROPOSAL ).getImage() ); Button reloadButton = new Button( availableObjectClassesInstantSearchComposite, SWT.PUSH | SWT.FLAT ); reloadButton.setToolTipText( Messages.getString( "NewEntryObjectclassWizardPage.ReloadSchema" ) ); //$NON-NLS-1$ reloadButton.setImage( BrowserCommonActivator.getDefault().getImage( BrowserCommonConstants.IMG_REFRESH ) ); reloadButton.addSelectionListener( new SelectionAdapter() { @Override public void widgetSelected( SelectionEvent e ) { IBrowserConnection browserConnection = wizard.getSelectedConnection(); ReloadSchemaRunnable runnable = new ReloadSchemaRunnable( browserConnection ); RunnableContextRunner.execute( runnable, wizard.getContainer(), true ); setVisible( true ); } } ); availableObjectClassesViewer = new TableViewer( availableObjectClassesComposite ); GridData data = new GridData( GridData.FILL_BOTH ); data.heightHint = SIZING_SELECTION_WIDGET_HEIGHT; data.widthHint = ( int ) ( SIZING_SELECTION_WIDGET_WIDTH * 0.4 ); availableObjectClassesViewer.getTable().setLayoutData( data ); availableObjectClassesViewer.setContentProvider( new ArrayContentProvider() ); availableObjectClassesViewer.setLabelProvider( labelProvider ); availableObjectClassesViewer.setSorter( new ViewerSorter() ); availableObjectClassesViewer.addFilter( new InstantSearchFilter( availableObjectClassesInstantSearch ) ); availableObjectClassesViewer.setInput( availableObjectClasses ); availableObjectClassesViewer.addDoubleClickListener( new IDoubleClickListener() { public void doubleClick( DoubleClickEvent event ) { add( event.getSelection() ); } } ); availableObjectClassesViewer.getTable().addKeyListener( new KeyAdapter() { public void keyPressed( KeyEvent e ) { if ( e.keyCode == SWT.ARROW_UP ) { if ( availableObjectClassesViewer.getTable().getSelectionIndex() <= 0 ) { availableObjectClassesInstantSearch.setFocus(); } } } } ); Composite buttonComposite = new Composite( composite, SWT.NONE ); gl = new GridLayout( 1, true ); buttonComposite.setLayout( gl ); data = new GridData( GridData.FILL_BOTH ); data.heightHint = SIZING_SELECTION_WIDGET_HEIGHT; // data.widthHint = (int)(SIZING_SELECTION_WIDGET_WIDTH * 0.2); data.horizontalAlignment = SWT.CENTER; buttonComposite.setLayoutData( data ); Label label0 = new Label( buttonComposite, SWT.NONE ); data = new GridData(); data.grabExcessHorizontalSpace = true; data.grabExcessVerticalSpace = true; label0.setLayoutData( data ); addButton = BaseWidgetUtils.createButton( buttonComposite, Messages .getString( "NewEntryObjectclassWizardPage.AddButton" ), 1 ); //$NON-NLS-1$ removeButton = BaseWidgetUtils.createButton( buttonComposite, Messages .getString( "NewEntryObjectclassWizardPage.RemoveButton" ), 1 ); //$NON-NLS-1$ Label label3 = new Label( buttonComposite, SWT.NONE ); data = new GridData(); data.grabExcessHorizontalSpace = true; data.grabExcessVerticalSpace = true; label3.setLayoutData( data ); addButton.addSelectionListener( new SelectionAdapter() { public void widgetSelected( SelectionEvent e ) { add( availableObjectClassesViewer.getSelection() ); } } ); removeButton.addSelectionListener( new SelectionAdapter() { public void widgetSelected( SelectionEvent e ) { remove( selectedObjectClassesViewer.getSelection() ); } } ); selectedObjectClassesViewer = new TableViewer( composite ); data = new GridData( GridData.FILL_BOTH ); data.heightHint = SIZING_SELECTION_WIDGET_HEIGHT; data.widthHint = ( int ) ( SIZING_SELECTION_WIDGET_WIDTH * 0.4 ); selectedObjectClassesViewer.getTable().setLayoutData( data ); selectedObjectClassesViewer.setContentProvider( new ArrayContentProvider() ); selectedObjectClassesViewer.setLabelProvider( labelProvider ); selectedObjectClassesViewer.setSorter( new ViewerSorter() ); selectedObjectClassesViewer.setInput( selectedObjectClasses ); selectedObjectClassesViewer.addDoubleClickListener( new IDoubleClickListener() { public void doubleClick( DoubleClickEvent event ) { remove( event.getSelection() ); } } ); setControl( composite ); } /** * Adds the selected object classes and all superiors * to the list of selected object classes. * * @param iselection the selection */ private void add( ISelection iselection ) { IStructuredSelection selection = ( IStructuredSelection ) iselection; Schema schema = wizard.getSelectedConnection().getSchema(); Iterator<?> it = selection.iterator(); while ( it.hasNext() ) { ObjectClass ocd = ( ObjectClass ) it.next(); if ( availableObjectClasses.contains( ocd ) && !selectedObjectClasses.contains( ocd ) ) { availableObjectClasses.remove( ocd ); selectedObjectClasses.add( ocd ); // recursively add superior object classes List<ObjectClass> superiorObjectClassDescriptions = SchemaUtils .getSuperiorObjectClassDescriptions( ocd, schema ); if ( !superiorObjectClassDescriptions.isEmpty() ) { add( new StructuredSelection( superiorObjectClassDescriptions ) ); } } } availableObjectClassesViewer.refresh(); selectedObjectClassesViewer.refresh(); validate(); if ( !"".equals( availableObjectClassesInstantSearch.getText() ) ) //$NON-NLS-1$ { availableObjectClassesInstantSearch.setText( "" ); //$NON-NLS-1$ availableObjectClassesInstantSearch.setFocus(); } } /** * Removes the selected object classes and all sub classes * from the list of selected object classes. * * @param iselection the iselection */ private void remove( ISelection iselection ) { IStructuredSelection selection = ( IStructuredSelection ) iselection; Schema schema = wizard.getSelectedConnection().getSchema(); Iterator<?> it = selection.iterator(); while ( it.hasNext() ) { ObjectClass ocd = ( ObjectClass ) it.next(); if ( !availableObjectClasses.contains( ocd ) && selectedObjectClasses.contains( ocd ) ) { selectedObjectClasses.remove( ocd ); availableObjectClasses.add( ocd ); // recursively remove sub object classes List<ObjectClass> subObjectClassDescriptions = SchemaUtils .getSuperiorObjectClassDescriptions( ocd, schema ); if ( !subObjectClassDescriptions.isEmpty() ) { remove( new StructuredSelection( subObjectClassDescriptions ) ); } } } // re-add superior object classes of remaining object classes List<ObjectClass> copy = new ArrayList<ObjectClass>( selectedObjectClasses ); for ( ObjectClass ocd : copy ) { List<ObjectClass> superiorObjectClassDescriptions = SchemaUtils .getSuperiorObjectClassDescriptions( ocd, schema ); if ( !superiorObjectClassDescriptions.isEmpty() ) { add( new StructuredSelection( superiorObjectClassDescriptions ) ); } } availableObjectClassesViewer.refresh(); selectedObjectClassesViewer.refresh(); validate(); } /** * The Class InstantSearchFilter. */ private class InstantSearchFilter extends ViewerFilter { /** The filter text. */ private Text filterText; /** * Creates a new instance of InstantSearchFilter. * * @param filterText the filter text */ private InstantSearchFilter( Text filterText ) { this.filterText = filterText; } /** * {@inheritDoc} */ public boolean select( Viewer viewer, Object parentElement, Object element ) { if ( element instanceof ObjectClass ) { ObjectClass ocd = ( ObjectClass ) element; Collection<String> lowerCaseIdentifiers = SchemaUtils.getLowerCaseIdentifiers( ocd ); for ( String s : lowerCaseIdentifiers ) { if ( Strings.toLowerCase( s ).startsWith( Strings.toLowerCase( filterText.getText() ) ) ) { return true; } } } return false; } } }