/*
* 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.valueeditors;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.directory.api.ldap.model.schema.AttributeType;
import org.apache.directory.api.ldap.model.schema.LdapSyntax;
import org.apache.directory.api.util.Strings;
import org.apache.directory.studio.ldapbrowser.common.BrowserCommonActivator;
import org.apache.directory.studio.ldapbrowser.common.BrowserCommonConstants;
import org.apache.directory.studio.ldapbrowser.core.model.AttributeHierarchy;
import org.apache.directory.studio.ldapbrowser.core.model.IAttribute;
import org.apache.directory.studio.ldapbrowser.core.model.IEntry;
import org.apache.directory.studio.ldapbrowser.core.model.IValue;
import org.apache.directory.studio.ldapbrowser.core.model.schema.Schema;
import org.apache.directory.studio.ldapbrowser.core.model.schema.SchemaUtils;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.plugin.AbstractUIPlugin;
/**
* A ValueEditorManager is used to manage value editors. It provides methods to get
* the best or alternative value editors for a given attribute or value. It takes
* user preferences into account when determine the best value editor. At least
* it provides default text and binary value editors.
*
* The available value editors are specified by the extension point
* <code>org.apache.directory.studio.valueeditors</code>.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class ValueEditorManager
{
private static final String ATTRIBUTE_TYPE = "attributeType"; //$NON-NLS-1$
private static final String ATTRIBUTE = "attribute"; //$NON-NLS-1$
private static final String SYNTAX_OID = "syntaxOID"; //$NON-NLS-1$
private static final String SYNTAX = "syntax"; //$NON-NLS-1$
private static final String ICON = "icon"; //$NON-NLS-1$
private static final String NAME = "name"; //$NON-NLS-1$
private static final String CLASS = "class"; //$NON-NLS-1$
/** The extension point ID for value editors */
private static final String EXTENSION_POINT = BrowserCommonConstants.EXTENSION_POINT_VALUE_EDITORS;
/** The composite used to create the value editors **/
private Composite parent;
/**
* The value editor explicitly selected by the user. If this
* member is not null it is always returned as current value editor.
*/
private IValueEditor userSelectedValueEditor;
/** The special value editor for multi-valued attributes */
private MultivaluedValueEditor multiValuedValueEditor;
/** The special value editor to edit the entry in an wizard */
private EntryValueEditor entryValueEditor;
/** The special value editor to rename the entry */
private RenameValueEditor renameValueEditor;
/** The default string editor for single-line values */
private IValueEditor defaultStringSingleLineValueEditor;
/** The default string editor for multi-line values */
private IValueEditor defaultStringMultiLineValueEditor;
/** The default binary editor */
private IValueEditor defaultBinaryValueEditor;
/** A map containing all available value editors. */
private Map<String, IValueEditor> class2ValueEditors;
/**
* Creates a new instance of ValueEditorManager.
*
* @param parent the composite used to create the value editors
*/
public ValueEditorManager( Composite parent, boolean useEntryValueEditor, boolean useRenameValueEditor )
{
this.parent = parent;
userSelectedValueEditor = null;
// init value editor map
class2ValueEditors = new HashMap<String, IValueEditor>();
Collection<IValueEditor> valueEditors = createValueEditors( parent );
for ( IValueEditor valueEditor : valueEditors )
{
class2ValueEditors.put( valueEditor.getClass().getName(), valueEditor );
}
// special case: multivalued editor
multiValuedValueEditor = new MultivaluedValueEditor( this.parent, this );
multiValuedValueEditor.setValueEditorName( Messages.getString( "ValueEditorManager.MulitivaluedEditor" ) ); //$NON-NLS-1$
multiValuedValueEditor.setValueEditorImageDescriptor( BrowserCommonActivator.getDefault().getImageDescriptor(
BrowserCommonConstants.IMG_MULTIVALUEDEDITOR ) );
// special case: entry editor
if ( useEntryValueEditor )
{
entryValueEditor = new EntryValueEditor( this.parent, this );
entryValueEditor.setValueEditorName( Messages.getString( "ValueEditorManager.EntryEditor" ) ); //$NON-NLS-1$
entryValueEditor.setValueEditorImageDescriptor( BrowserCommonActivator.getDefault().getImageDescriptor(
BrowserCommonConstants.IMG_ENTRY_EDITOR ) );
}
// special case: rename editor
if ( useRenameValueEditor )
{
renameValueEditor = new RenameValueEditor( this.parent, this );
renameValueEditor.setValueEditorName( Messages.getString( "ValueEditorManager.RenameEditor" ) ); //$NON-NLS-1$
renameValueEditor.setValueEditorImageDescriptor( BrowserCommonActivator.getDefault().getImageDescriptor(
BrowserCommonConstants.IMG_RENAME ) );
}
// get default editors from value editor map
defaultStringSingleLineValueEditor = class2ValueEditors.get( InPlaceTextValueEditor.class.getName() );
defaultStringMultiLineValueEditor = class2ValueEditors.get( TextValueEditor.class.getName() );
defaultBinaryValueEditor = class2ValueEditors.get( HexValueEditor.class.getName() );
}
/**
* Disposes all value editors.
*/
public void dispose()
{
if ( parent != null )
{
userSelectedValueEditor = null;
multiValuedValueEditor.dispose();
if ( entryValueEditor != null )
{
entryValueEditor.dispose();
}
if ( renameValueEditor != null )
{
renameValueEditor.dispose();
}
defaultStringSingleLineValueEditor.dispose();
defaultStringMultiLineValueEditor.dispose();
defaultBinaryValueEditor.dispose();
for ( IValueEditor ve : class2ValueEditors.values() )
{
ve.dispose();
}
parent = null;
}
}
/**
* Sets the value editor explicitly selected by the user. Set
* userSelectedValueEditor to null to remove the selection.
*
* @param userSelectedValueEditor the user selected value editor, may be null.
*/
public void setUserSelectedValueEditor( IValueEditor userSelectedValueEditor )
{
this.userSelectedValueEditor = userSelectedValueEditor;
}
/**
* Gets the value editor explicitly selected by the user.
*
* @return the user selected value editor, null if non is set
*/
public IValueEditor getUserSelectedValueEditor()
{
return userSelectedValueEditor;
}
/**
* Returns the current (best) value editor for the given attribute.
*
* <ol>
* <li>If a user selected value editor is selected, this is returned.
* <li>If a specific value editor is defined for the attribute type this
* value editor is returned. See preferences.
* <li>If a specific value editor is defined for the attribute's syntax this
* value editor is returned. See preferences.
* <li>Otherwise a default value editor is returned. If the attribute is
* binary the default Hex Editor is returned. Otherwise the default
* Text Editor is returned.
* </ol>
*
* @param schema the schema
* @param attributeType the attribute type
* @return the current value editor
*/
public IValueEditor getCurrentValueEditor( Schema schema, String attributeType )
{
// check user-selected (forced) value editor
if ( userSelectedValueEditor != null )
{
return userSelectedValueEditor;
}
AttributeType atd = schema.getAttributeTypeDescription( attributeType );
// check attribute preferences
Map<String, String> attributeValueEditorMap = BrowserCommonActivator.getDefault().getValueEditorsPreferences()
.getAttributeValueEditorMap();
String oidStr = Strings.toLowerCase( atd.getOid() );
if ( atd.getOid() != null && attributeValueEditorMap.containsKey( oidStr ) )
{
return ( IValueEditor ) class2ValueEditors.get( attributeValueEditorMap.get( oidStr ) );
}
List<String> names = atd.getNames();
for ( String name : names )
{
String nameStr = Strings.toLowerCase( name );
if ( attributeValueEditorMap.containsKey( nameStr ) )
{
return ( IValueEditor ) class2ValueEditors.get( attributeValueEditorMap.get( nameStr ) );
}
}
// check syntax preferences
String syntaxNumericOid = SchemaUtils.getSyntaxNumericOidTransitive( atd, schema );
Map<String, String> syntaxValueEditorMap = BrowserCommonActivator.getDefault().getValueEditorsPreferences()
.getSyntaxValueEditorMap();
String syntaxtNumericOidStr = Strings.toLowerCase( syntaxNumericOid );
if ( ( syntaxNumericOid != null ) && syntaxValueEditorMap.containsKey( syntaxtNumericOidStr ) )
{
return ( IValueEditor ) class2ValueEditors.get( syntaxValueEditorMap.get( syntaxtNumericOidStr ) );
}
// return default
LdapSyntax lsd = schema.getLdapSyntaxDescription( syntaxNumericOid );
if ( SchemaUtils.isBinary( lsd ) )
{
return defaultBinaryValueEditor;
}
else
{
return defaultStringSingleLineValueEditor;
}
}
/**
* Returns the current (best) value editor for the given attribute.
*
* @param entry the entry
* @param attributeType the attributge type
* @return the current value editor
* @see #getCurrentValueEditor( Schema, String )
*/
public IValueEditor getCurrentValueEditor( IEntry entry, String attributeType )
{
return getCurrentValueEditor( entry.getBrowserConnection().getSchema(), attributeType );
}
/**
* Returns the current (best) value editor for the given value.
*
* @param value the value
* @return the current value editor
* @see #getCurrentValueEditor( Schema, String )
*/
public IValueEditor getCurrentValueEditor( IValue value )
{
IAttribute attribute = value.getAttribute();
IValueEditor ve = getCurrentValueEditor( attribute.getEntry(), attribute.getDescription() );
// special case objectClass: always return entry editor
if ( userSelectedValueEditor == null )
{
if ( attribute.isObjectClassAttribute() && ( entryValueEditor != null ) )
{
return entryValueEditor;
}
// special case Rdn attribute: always return rename editor
if ( value.isRdnPart() && ( renameValueEditor != null ) )
{
return renameValueEditor;
}
}
// here the value is known, we can check for single-line or multi-line
if ( ve == defaultStringSingleLineValueEditor )
{
String stringValue = value.getStringValue();
if ( ( stringValue.indexOf( '\n' ) == -1 ) && ( stringValue.indexOf( '\r' ) == -1 ) )
{
ve = defaultStringSingleLineValueEditor;
}
else
{
ve = defaultStringMultiLineValueEditor;
}
}
return ve;
}
/**
* Returns the current (best) value editor for the given attribute.
*
* @param attributeHierarchy the attribute hierarchy
* @return the current value editor
* @see #getCurrentValueEditor( Schema, String )
*/
public IValueEditor getCurrentValueEditor( AttributeHierarchy attributeHierarchy )
{
if ( attributeHierarchy == null )
{
return null;
}
else if ( ( userSelectedValueEditor == null ) && attributeHierarchy.getAttribute().isObjectClassAttribute()
&& entryValueEditor != null )
{
// special case objectClass: always return entry editor
return entryValueEditor;
}
else if ( ( userSelectedValueEditor == entryValueEditor ) && ( entryValueEditor != null ) )
{
// special case objectClass: always return entry editor
return entryValueEditor;
}
else if ( ( attributeHierarchy.size() == 1 ) && ( attributeHierarchy.getAttribute().getValueSize() == 0 ) )
{
return getCurrentValueEditor( attributeHierarchy.getAttribute().getEntry(), attributeHierarchy
.getAttribute().getDescription() );
}
else if ( ( attributeHierarchy.size() == 1 ) &&
( attributeHierarchy.getAttribute().getValueSize() == 1 ) &&
attributeHierarchy.getAttributeDescription().equalsIgnoreCase(
attributeHierarchy.getAttribute().getValues()[0].getAttribute().getDescription() ) )
{
// special case Rdn: always return MV-editor
if ( ( userSelectedValueEditor == null ) && attributeHierarchy.getAttribute().getValues()[0].isRdnPart() )
{
if ( renameValueEditor != null )
{
return renameValueEditor;
}
else
{
return multiValuedValueEditor;
}
}
return getCurrentValueEditor( attributeHierarchy.getAttribute().getValues()[0] );
}
else
{
return multiValuedValueEditor;
}
}
/**
* Returns alternative value editors for the given attribute. For now these
* are the three default editors.
*
* @param entry the entry
* @param attributeName the attribute
* @return alternative value editors
*/
public IValueEditor[] getAlternativeValueEditors( IEntry entry, String attributeName )
{
Schema schema = entry.getBrowserConnection().getSchema();
return getAlternativeValueEditors( schema, attributeName );
}
/**
* Returns alternative value editors for the given attribute. For now these
* are the three default editors.
*
* @param schema the schema
* @param attributeName the attribute
* @return the alternative value editors
*/
public IValueEditor[] getAlternativeValueEditors( Schema schema, String attributeName )
{
List<IValueEditor> alternativeList = new ArrayList<IValueEditor>();
AttributeType atd = schema.getAttributeTypeDescription( attributeName );
if ( SchemaUtils.isBinary( atd, schema ) )
{
alternativeList.add( defaultBinaryValueEditor );
alternativeList.add( defaultStringSingleLineValueEditor );
alternativeList.add( defaultStringMultiLineValueEditor );
}
else if ( SchemaUtils.isString( atd, schema ) )
{
alternativeList.add( defaultStringSingleLineValueEditor );
alternativeList.add( defaultStringMultiLineValueEditor );
alternativeList.add( defaultBinaryValueEditor );
}
alternativeList.add( multiValuedValueEditor );
alternativeList.remove( getCurrentValueEditor( schema, attributeName ) );
return alternativeList.toArray( new IValueEditor[alternativeList.size()] );
}
/**
* Returns alternative value editors for the given value. For now these
* are the three default editors.
*
* @param value the value
* @return the alternative value editors
*/
public IValueEditor[] getAlternativeValueEditors( IValue value )
{
List<IValueEditor> alternativeList = new ArrayList<IValueEditor>();
if ( value.isBinary() )
{
alternativeList.add( defaultBinaryValueEditor );
alternativeList.add( defaultStringSingleLineValueEditor );
alternativeList.add( defaultStringMultiLineValueEditor );
}
else if ( value.isString() )
{
alternativeList.add( defaultStringSingleLineValueEditor );
alternativeList.add( defaultStringMultiLineValueEditor );
alternativeList.add( defaultBinaryValueEditor );
}
alternativeList.add( multiValuedValueEditor );
alternativeList.remove( getCurrentValueEditor( value ) );
return alternativeList.toArray( new IValueEditor[alternativeList.size()] );
}
/**
* Returns alternative value editors for the given value. For now these
* are the three default editors.
*
* @param ah the attribute hierarchy
* @return alternative value editors
*/
public IValueEditor[] getAlternativeValueEditors( AttributeHierarchy ah )
{
if ( ah == null )
{
return new IValueEditor[0];
}
// special case Rdn: no alternative to the rename editor, except the MV editor
// perhaps this should be moved somewhere else
if ( multiValuedValueEditor != null )
{
for ( IAttribute attribute : ah )
{
for ( IValue value : attribute.getValues() )
{
if ( value.isRdnPart() )
{
return new IValueEditor[]
{ multiValuedValueEditor };
}
}
}
}
// special case objectClass: no alternative to the entry editor
// perhaps this should be moved somewhere else
for ( IAttribute attribute : ah )
{
if ( attribute.isObjectClassAttribute() )
{
return new IValueEditor[0];
}
}
if ( ( ah.size() == 1 ) && ( ah.getAttribute().getValueSize() == 0 ) )
{
return getAlternativeValueEditors( ah.getAttribute().getEntry(), ah.getAttribute().getDescription() );
}
else if ( ( ah.size() == 1 ) &&
( ah.getAttribute().getValueSize() == 1 ) &&
ah.getAttributeDescription().equalsIgnoreCase(
ah.getAttribute().getValues()[0].getAttribute().getDescription() ) )
{
return getAlternativeValueEditors( ah.getAttribute().getValues()[0] );
}
else
{
return new IValueEditor[0];
}
}
/**
* Returns all available value editors.
*
* @return all available value editors
*/
public IValueEditor[] getAllValueEditors()
{
// use a set to avoid double entries
Set<IValueEditor> list = new LinkedHashSet<IValueEditor>();
list.add( defaultStringSingleLineValueEditor );
list.add( defaultStringMultiLineValueEditor );
list.add( defaultBinaryValueEditor );
list.addAll( class2ValueEditors.values() );
list.add( multiValuedValueEditor );
if ( entryValueEditor != null )
{
list.add( entryValueEditor );
}
if ( renameValueEditor != null )
{
list.add( renameValueEditor );
}
return list.toArray( new IValueEditor[list.size()] );
}
/**
* Returns the default binary editor (a HexEditor).
*
* @return the default binary editor
*/
public IValueEditor getDefaultBinaryValueEditor()
{
return defaultBinaryValueEditor;
}
/**
* Returns the default string editor (a TextEditor).
*
* @return the default string editor
*/
public IValueEditor getDefaultStringValueEditor()
{
return defaultStringMultiLineValueEditor;
}
/**
* Returns the multi-valued editor.
*
* @return the multi-valued editor
*/
public MultivaluedValueEditor getMultiValuedValueEditor()
{
return multiValuedValueEditor;
}
/**
* Returns the entry value editor.
*
* @return the entry value editor
*/
public EntryValueEditor getEntryValueEditor()
{
return entryValueEditor;
}
/**
* Returns the rename value editor.
*
* @return the rename value editor
*/
public RenameValueEditor getRenameValueEditor()
{
return renameValueEditor;
}
/**
* Creates and returns the value editors specified by value editors extensions.
*
* @param parent the parent composite
* @return the value editors
*/
private Collection<IValueEditor> createValueEditors( Composite parent )
{
Collection<IValueEditor> valueEditors = new ArrayList<IValueEditor>();
Collection<ValueEditorExtension> valueEditorExtensions = getValueEditorExtensions();
for ( ValueEditorExtension vee : valueEditorExtensions )
{
try
{
IValueEditor valueEditor = ( IValueEditor ) vee.member.createExecutableExtension( CLASS );
valueEditor.create( parent );
valueEditor.setValueEditorName( vee.name );
valueEditor.setValueEditorImageDescriptor( vee.icon );
valueEditors.add( valueEditor );
}
catch ( Exception e )
{
BrowserCommonActivator.getDefault().getLog().log(
new Status( IStatus.ERROR, BrowserCommonConstants.PLUGIN_ID, 1, Messages
.getString( "ValueEditorManager.UnableToCreateValueEditor" ) //$NON-NLS-1$
+ vee.className, e ) );
}
}
return valueEditors;
}
/**
* Returns all value editor extensions specified by value editor extensions.
*
* @return the value editor extensions
*/
public static Collection<ValueEditorExtension> getValueEditorExtensions()
{
Collection<ValueEditorExtension> valueEditorExtensions = new ArrayList<ValueEditorExtension>();
IExtensionRegistry registry = Platform.getExtensionRegistry();
IExtensionPoint extensionPoint = registry.getExtensionPoint( EXTENSION_POINT );
IConfigurationElement[] members = extensionPoint.getConfigurationElements();
// For each extension:
for ( IConfigurationElement member : members )
{
ValueEditorExtension proxy = new ValueEditorExtension();
valueEditorExtensions.add( proxy );
IExtension extension = member.getDeclaringExtension();
String extendingPluginId = extension.getNamespaceIdentifier();
proxy.member = member;
proxy.name = member.getAttribute( NAME );
String iconPath = member.getAttribute( ICON );
proxy.icon = AbstractUIPlugin.imageDescriptorFromPlugin( extendingPluginId, iconPath );
if ( proxy.icon == null )
{
proxy.icon = ImageDescriptor.getMissingImageDescriptor();
}
proxy.className = member.getAttribute( CLASS );
IConfigurationElement[] children = member.getChildren();
for ( IConfigurationElement child : children )
{
String type = child.getName();
if ( SYNTAX.equals( type ) )
{
String syntaxOID = child.getAttribute( SYNTAX_OID );
proxy.syntaxOids.add( syntaxOID );
}
else if ( ATTRIBUTE.equals( type ) )
{
String attributeType = child.getAttribute( ATTRIBUTE_TYPE );
proxy.attributeTypes.add( attributeType );
}
}
}
return valueEditorExtensions;
}
/**
* This class is a bean to hold the data defined in value editor extension
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public static class ValueEditorExtension
{
/** The name. */
public String name = null;
/** The icon. */
public ImageDescriptor icon = null;
/** The class name. */
public String className = null;
/** The syntax oids. */
public Collection<String> syntaxOids = new ArrayList<String>( 3 );
/** The attribute types. */
public Collection<String> attributeTypes = new ArrayList<String>( 3 );
/** The configuration element. */
private IConfigurationElement member = null;
/**
* @see Object#toString()
*/
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( '<' );
sb.append( name ).append( ", " );
sb.append( className );
if ( ( attributeTypes != null ) && ( attributeTypes.size() > 0 ) )
{
sb.append( ", {" );
boolean isFirst = true;
for ( String attributeType : attributeTypes )
{
if ( isFirst )
{
isFirst = false;
}
else
{
sb.append( ", " );
}
sb.append( attributeType );
}
sb.append( '}' );
}
if ( ( syntaxOids != null ) && ( syntaxOids.size() > 0 ) )
{
sb.append( ", {" );
boolean isFirst = true;
for ( String syntaxOid : syntaxOids )
{
if ( isFirst )
{
isFirst = false;
}
else
{
sb.append( ", " );
}
sb.append( syntaxOid );
}
sb.append( '}' );
}
sb.append( '>' );
return sb.toString();
}
}
}