org.apache.directory.studio.valueeditors.ValueEditorManager.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.directory.studio.valueeditors.ValueEditorManager.java

Source

/*
 *  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();
        }
    }
}