org.eclipse.wb.internal.core.model.description.helpers.ComponentDescriptionHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wb.internal.core.model.description.helpers.ComponentDescriptionHelper.java

Source

/*******************************************************************************
 * Copyright (c) 2011 Google, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Google, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.wb.internal.core.model.description.helpers;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

import org.eclipse.wb.internal.core.model.description.AbstractInvocationDescription;
import org.eclipse.wb.internal.core.model.description.ComponentDescription;
import org.eclipse.wb.internal.core.model.description.ComponentDescriptionKey;
import org.eclipse.wb.internal.core.model.description.ConfigurablePropertyDescription;
import org.eclipse.wb.internal.core.model.description.ConstructorDescription;
import org.eclipse.wb.internal.core.model.description.CreationDescription;
import org.eclipse.wb.internal.core.model.description.CreationInvocationDescription;
import org.eclipse.wb.internal.core.model.description.GenericPropertyDescription;
import org.eclipse.wb.internal.core.model.description.IDescriptionProcessor;
import org.eclipse.wb.internal.core.model.description.ParameterDescription;
import org.eclipse.wb.internal.core.model.description.ToolkitDescription;
import org.eclipse.wb.internal.core.model.description.factory.FactoryMethodDescription;
import org.eclipse.wb.internal.core.model.description.internal.AbstractConfigurableDescription;
import org.eclipse.wb.internal.core.model.description.resource.ClassResourceInfo;
import org.eclipse.wb.internal.core.model.description.resource.ResourceInfo;
import org.eclipse.wb.internal.core.model.description.rules.ConfigurableObjectListParameterRule;
import org.eclipse.wb.internal.core.model.description.rules.ConfigurableObjectParameterRule;
import org.eclipse.wb.internal.core.model.description.rules.ConfigurablePropertyRule;
import org.eclipse.wb.internal.core.model.description.rules.ConstructorRule;
import org.eclipse.wb.internal.core.model.description.rules.CreationTagRule;
import org.eclipse.wb.internal.core.model.description.rules.CreationTypeParametersRule;
import org.eclipse.wb.internal.core.model.description.rules.ExposingRulesRule;
import org.eclipse.wb.internal.core.model.description.rules.MethodOrderDefaultRule;
import org.eclipse.wb.internal.core.model.description.rules.MethodOrderMethodRule;
import org.eclipse.wb.internal.core.model.description.rules.MethodOrderMethodsRule;
import org.eclipse.wb.internal.core.model.description.rules.MethodOrderMethodsSignatureRule;
import org.eclipse.wb.internal.core.model.description.rules.MethodPropertyRule;
import org.eclipse.wb.internal.core.model.description.rules.MethodRule;
import org.eclipse.wb.internal.core.model.description.rules.MethodSinglePropertyRule;
import org.eclipse.wb.internal.core.model.description.rules.MethodTagRule;
import org.eclipse.wb.internal.core.model.description.rules.MethodsOperationRule;
import org.eclipse.wb.internal.core.model.description.rules.ModelClassRule;
import org.eclipse.wb.internal.core.model.description.rules.MorphingNoInheritRule;
import org.eclipse.wb.internal.core.model.description.rules.MorphingTargetRule;
import org.eclipse.wb.internal.core.model.description.rules.ObjectCreateRule;
import org.eclipse.wb.internal.core.model.description.rules.ParameterEditorRule;
import org.eclipse.wb.internal.core.model.description.rules.ParameterTagRule;
import org.eclipse.wb.internal.core.model.description.rules.PropertyAccessRule;
import org.eclipse.wb.internal.core.model.description.rules.PropertyCategoryRule;
import org.eclipse.wb.internal.core.model.description.rules.PropertyDefaultRule;
import org.eclipse.wb.internal.core.model.description.rules.PropertyEditorRule;
import org.eclipse.wb.internal.core.model.description.rules.PropertyGetterRule;
import org.eclipse.wb.internal.core.model.description.rules.PropertyTagRule;
import org.eclipse.wb.internal.core.model.description.rules.PublicFieldPropertiesRule;
import org.eclipse.wb.internal.core.model.description.rules.SetClassPropertyRule;
import org.eclipse.wb.internal.core.model.description.rules.SetListedPropertiesRule;
import org.eclipse.wb.internal.core.model.description.rules.StandardBeanPropertiesAdvancedRule;
import org.eclipse.wb.internal.core.model.description.rules.StandardBeanPropertiesHiddenRule;
import org.eclipse.wb.internal.core.model.description.rules.StandardBeanPropertiesNoDefaultValueRule;
import org.eclipse.wb.internal.core.model.description.rules.StandardBeanPropertiesNormalRule;
import org.eclipse.wb.internal.core.model.description.rules.StandardBeanPropertiesPreferredRule;
import org.eclipse.wb.internal.core.model.description.rules.StandardBeanPropertiesRule;
import org.eclipse.wb.internal.core.model.description.rules.StandardBeanPropertyTagRule;
import org.eclipse.wb.internal.core.model.description.rules.ToolkitRule;
import org.eclipse.wb.internal.core.utils.IOUtils2;
import org.eclipse.wb.internal.core.utils.ast.AstEditor;
import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils;
import org.eclipse.wb.internal.core.utils.ast.AstParser;
import org.eclipse.wb.internal.core.utils.ast.DomGenerics;
import org.eclipse.wb.internal.core.utils.check.Assert;
import org.eclipse.wb.internal.core.utils.exception.DesignerException;
import org.eclipse.wb.internal.core.utils.exception.ICoreExceptionConstants;
import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils;
import org.eclipse.wb.internal.core.utils.execution.RunnableEx;
import org.eclipse.wb.internal.core.utils.external.ExternalFactoriesHelper;
import org.eclipse.wb.internal.core.utils.jdt.core.CodeUtils;
import org.eclipse.wb.internal.core.utils.reflect.ClassMap;
import org.eclipse.wb.internal.core.utils.reflect.IntrospectionHelper;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
import org.eclipse.wb.internal.core.utils.state.EditorState;
import org.eclipse.wb.internal.core.utils.ui.ImageDisposer;

import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.swt.graphics.Image;

import org.apache.commons.digester.AbstractObjectCreationFactory;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.Rule;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.impl.NoOpLog;
import org.osgi.framework.Bundle;
import org.xml.sax.Attributes;
import org.xml.sax.SAXParseException;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

/**
 * Helper for accessing descriptions of components - {@link ComponentDescription}.
 * 
 * @author scheglov_ke
 * @coverage core.model.description
 */
public final class ComponentDescriptionHelper {
    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructor
    //
    ////////////////////////////////////////////////////////////////////////////
    private ComponentDescriptionHelper() {
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Access
    //
    ////////////////////////////////////////////////////////////////////////////
    private static final ClassMap<ComponentDescription> m_getDescription_Class = ClassMap.create();

    /**
     * Returns the factory-method specific {@link ComponentDescription}.
     * <p>
     * Sometimes you want to add specific tweaks for component, when it is created using factory. For
     * example - hide some properties, copy factory properties, etc.
     */
    public static ComponentDescription getDescription(AstEditor editor,
            FactoryMethodDescription factoryMethodDescription) throws Exception {
        Class<?> componentClass = factoryMethodDescription.getReturnClass();
        // prepare description key
        ComponentDescriptionKey key;
        {
            String signature = factoryMethodDescription.getSignature();
            String signatureUnix = StringUtils.replaceChars(signature, "(,)", "___");
            Class<?> declaringClass = factoryMethodDescription.getDeclaringClass();
            ComponentDescriptionKey declaringKey = new ComponentDescriptionKey(declaringClass);
            key = new ComponentDescriptionKey(componentClass, declaringKey, signatureUnix);
        }
        // get key specific description
        return getKeySpecificDescription(editor, componentClass, key);
    }

    /**
     * @param editor
     *          the {@link AstEditor} in context of which we work now.
     * @param hostDescription
     *          the {@link ComponentDescription} which child component we expose.
     * @param method
     *          the {@link Method} that exposes component.
     * 
     * @return the {@link ComponentDescription} of component exposed using given {@link Method}.
     * @throws Exception
     *           if no {@link ComponentDescription} can be found.
     */
    public static ComponentDescription getDescription(AstEditor editor, ComponentDescription hostDescription,
            Method method) throws Exception {
        Class<?> componentClass = method.getReturnType();
        // prepare description key
        ComponentDescriptionKey key;
        {
            String suffix = method.getName();
            key = new ComponentDescriptionKey(componentClass, hostDescription.getKey(), suffix);
        }
        // get key specific description
        return getKeySpecificDescription(editor, componentClass, key);
    }

    /**
     * @param editor
     *          the {@link AstEditor} in context of which we work now.
     * @param hostDescription
     *          the {@link ComponentDescription} that has method with given parameter.
     * @param parameter
     *          the {@link SingleVariableDeclaration} parameter that considered as component.
     * 
     * @return the {@link ComponentDescription} of component represented by given
     *         {@link SingleVariableDeclaration}.
     * @throws Exception
     *           if no {@link ComponentDescription} can be found.
     */
    public static ComponentDescription getDescription(AstEditor editor, ComponentDescription hostDescription,
            SingleVariableDeclaration parameter) throws Exception {
        // prepare parameter Class
        Class<?> parameterClass;
        {
            String parameterClassName = AstNodeUtils.getFullyQualifiedName(parameter.getType(), true);
            parameterClass = EditorState.get(editor).getEditorLoader().loadClass(parameterClassName);
        }
        // prepare suffix
        String suffix;
        {
            MethodDeclaration methodDeclaration = (MethodDeclaration) parameter.getParent();
            String signature = AstNodeUtils.getMethodSignature(methodDeclaration);
            String signatureUnix = StringUtils.replaceChars(signature, "(,)", "___");
            int parameterIndex = DomGenerics.parameters(methodDeclaration).indexOf(parameter);
            suffix = signatureUnix + "." + parameterIndex;
        }
        // prepare DescriptionInfo's for parameter in inheritance hierarchy
        List<ClassResourceInfo> additionalDescriptions = Lists.newArrayList();
        {
            Class<?> hostComponentClass = hostDescription.getComponentClass();
            List<Class<?>> types = ReflectionUtils.getSuperHierarchy(hostComponentClass);
            types = Lists.reverse(types);
            for (Class<?> type : types) {
                ComponentDescriptionKey hostKey = new ComponentDescriptionKey(type);
                // prepare specific ResourceInfo
                ResourceInfo resourceInfo;
                {
                    EditorState state = EditorState.get(editor);
                    ILoadingContext context = EditorStateLoadingContext.get(state);
                    String descriptionPath = hostKey.getName() + "." + suffix + ".wbp-component.xml";
                    resourceInfo = DescriptionHelper.getResourceInfo(context, type, descriptionPath);
                }
                // add specific DescriptionInfo
                if (resourceInfo != null) {
                    ClassResourceInfo descriptionInfo = new ClassResourceInfo(parameterClass, resourceInfo);
                    additionalDescriptions.add(descriptionInfo);
                }
            }
        }
        // get key specific description
        if (additionalDescriptions.isEmpty()) {
            return getDescription(editor, parameterClass);
        } else {
            ComponentDescriptionKey key = new ComponentDescriptionKey(parameterClass, hostDescription.getKey(),
                    suffix);
            return getDescription0(editor, key, additionalDescriptions);
        }
    }

    /**
     * @param editor
     *          the {@link AstEditor} in context of which we work now.
     * @param componentClass
     *          the {@link Class} of component to get description.
     * 
     * @return the {@link ComponentDescription} of component with given {@link Class}.
     * @throws Exception
     *           if no {@link ComponentDescription} can be found.
     */
    public static ComponentDescription getDescription(AstEditor editor, Class<?> componentClass) throws Exception {
        ComponentDescription description = m_getDescription_Class.get(componentClass);
        if (description == null) {
            description = getDescription0(editor, componentClass);
            m_getDescription_Class.put(componentClass, description);
        }
        return description;
    }

    /**
     * Implementation for {@link #getDescription(AstEditor, Class)}.
     */
    private static ComponentDescription getDescription0(AstEditor editor, Class<?> componentClass)
            throws Exception {
        // we should use component class that can be loaded, for example ignore anonymous classes
        for (;; componentClass = componentClass.getSuperclass()) {
            String componentClassName = componentClass.getName();
            // stop if not an inner class
            int index = componentClassName.indexOf('$');
            if (index == -1) {
                break;
            }
            // stop if anonymous implementation of some interface
            if (componentClass.getInterfaces().length != 0) {
                break;
            }
            // stop if not an anonymous class
            String innerPart = componentClassName.substring(index + 1);
            if (!StringUtils.isNumeric(innerPart)) {
                break;
            }
        }
        // OK, get description
        ComponentDescriptionKey key = new ComponentDescriptionKey(componentClass);
        return getDescription0(editor, key, ImmutableList.<ClassResourceInfo>of());
    }

    /**
     * @param editor
     *          the {@link AstEditor} in context of which we work now.
     * @param componentClassName
     *          the name of {@link Class} of component to get description.
     * 
     * @return the {@link ComponentDescription} of component with given {@link Class}.
     * @throws Exception
     *           if no {@link ComponentDescription} can be found.
     */
    public static ComponentDescription getDescription(AstEditor editor, String componentClassName)
            throws Exception {
        Class<?> componentClass = EditorState.get(editor).getEditorLoader().loadClass(componentClassName);
        return getDescription(editor, componentClass);
    }

    /**
     * @return the {@link ComponentDescription} that is specific to given
     *         {@link ComponentDescriptionKey}, if exists, or just {@link ComponentDescription} for
     *         given component {@link Class}.
     */
    private static ComponentDescription getKeySpecificDescription(AstEditor editor, Class<?> componentClass,
            ComponentDescriptionKey key) throws Exception {
        // prepare optional key-specific ResourceInfo
        ResourceInfo resourceInfo;
        {
            EditorState state = EditorState.get(editor);
            ILoadingContext context = EditorStateLoadingContext.get(state);
            String descriptionPath = key.getName() + ".wbp-component.xml";
            resourceInfo = DescriptionHelper.getResourceInfo(context, componentClass, descriptionPath);
        }
        // if no key-specific, use pure type description
        if (resourceInfo == null) {
            return getDescription(editor, componentClass);
        }
        // OK, get key-specific description
        ClassResourceInfo descriptionInfo = new ClassResourceInfo(componentClass, resourceInfo);
        return getDescription0(editor, key, ImmutableList.of(descriptionInfo));
    }

    /**
     * @param editor
     *          the {@link AstEditor} in context of which we work now.
     * @param key
     *          the {@link ComponentDescriptionKey} of requested {@link ComponentDescription}.
     * @param additionalDescriptionInfos
     *          additional {@link ClassResourceInfo}'s to parse after {@link ClassResourceInfo}'s
     *          collected for component {@link Class}. May be empty, but not <code>null</code>.
     * 
     * @return the {@link ComponentDescription} of component with given {@link Class}.
     * @throws Exception
     *           if no {@link ComponentDescription} can be found.
     */
    private static ComponentDescription getDescription0(AstEditor editor, ComponentDescriptionKey key,
            List<ClassResourceInfo> additionalDescriptionInfos) throws Exception {
        EditorState state = EditorState.get(editor);
        ILoadingContext context = EditorStateLoadingContext.get(state);
        Class<?> componentClass = key.getComponentClass();
        //
        try {
            // prepare result description
            ComponentDescription componentDescription = new ComponentDescription(key);
            addConstructors(editor.getJavaProject(), componentDescription);
            componentDescription.setBeanInfo(ReflectionUtils.getBeanInfo(componentClass));
            componentDescription.setBeanDescriptor(new IntrospectionHelper(componentClass).getBeanDescriptor());
            // prepare list of description resources, from generic to specific
            LinkedList<ClassResourceInfo> descriptionInfos;
            {
                descriptionInfos = Lists.newLinkedList();
                DescriptionHelper.addDescriptionResources(descriptionInfos, context, componentClass);
                Assert.isTrueException(!descriptionInfos.isEmpty(),
                        ICoreExceptionConstants.DESCRIPTION_NO_DESCRIPTIONS, componentClass.getName());
                // at last append additional description resource
                descriptionInfos.addAll(additionalDescriptionInfos);
            }
            // prepare Digester
            Digester digester;
            {
                digester = new Digester();
                digester.setLogger(new NoOpLog());
                addRules(digester, editor, componentClass);
            }
            // read descriptions from generic to specific
            for (ClassResourceInfo descriptionInfo : descriptionInfos) {
                ResourceInfo resourceInfo = descriptionInfo.resource;
                // read next description
                {
                    componentDescription.setCurrentClass(descriptionInfo.clazz);
                    digester.push(componentDescription);
                    // do parse
                    InputStream is = resourceInfo.getURL().openStream();
                    try {
                        digester.parse(is);
                    } finally {
                        IOUtils.closeQuietly(is);
                    }
                }
                // clear parts that can not be inherited
                if (descriptionInfo.clazz == componentClass) {
                    setDescriptionWithInnerTags(componentDescription, resourceInfo);
                } else {
                    componentDescription.clearCreations();
                    componentDescription.setDescription(null);
                }
            }
            // set toolkit
            if (componentDescription.getToolkit() == null) {
                for (int i = descriptionInfos.size() - 1; i >= 0; i--) {
                    ClassResourceInfo descriptionInfo = descriptionInfos.get(i);
                    ToolkitDescription toolkit = descriptionInfo.resource.getToolkit();
                    if (toolkit != null) {
                        componentDescription.setToolkit(toolkit);
                        break;
                    }
                }
                Assert.isTrueException(componentDescription.getToolkit() != null,
                        ICoreExceptionConstants.DESCRIPTION_NO_TOOLKIT, componentClass.getName());
            }
            // icon, default creation
            setIcon(context, componentDescription, componentClass);
            configureDefaultCreation(componentDescription);
            // final operations
            {
                Assert.isNotNull(componentDescription.getModelClass());
                componentDescription.joinProperties();
            }
            // add to caches
            if (key.isPureComponent() && !"true".equals(componentDescription.getParameter("dontCacheDescription"))
                    && shouldCacheDescriptions_inPackage(descriptionInfos.getLast(), componentClass)) {
                componentDescription.setCached(true);
            }
            // mark for caching presentation
            if (shouldCachePresentation(descriptionInfos.getLast(), componentClass)) {
                componentDescription.setPresentationCached(true);
            }
            // use processors
            for (IDescriptionProcessor processor : getDescriptionProcessors()) {
                processor.process(editor, componentDescription);
            }
            // well, we have result
            return componentDescription;
        } catch (SAXParseException e) {
            throw new DesignerException(ICoreExceptionConstants.DESCRIPTION_LOAD_ERROR, e.getException(),
                    componentClass.getName());
        }
    }

    /**
     * Usually XML parsers don't allow to get content of element with all inner tags, but we want
     * these tags for description. So, we need special way to get description. Right now it is not
     * very accurate, but may be will enough for practical purposes.
     */
    private static void setDescriptionWithInnerTags(ComponentDescription componentDescription,
            ResourceInfo resourceInfo) throws Exception {
        InputStream stream = resourceInfo.getURL().openStream();
        String string = IOUtils2.readString(stream);
        String description = StringUtils.substringBetween(string, "<description>", "</description>");
        if (description != null) {
            componentDescription.setDescription(description);
        }
    }

    /**
     * Configures default {@link CreationDescription} with valid source.
     */
    private static void configureDefaultCreation(ComponentDescription componentDescription) {
        Class<?> componentClass = componentDescription.getComponentClass();
        // prepare shortest constructor
        Constructor<?> constructor = ReflectionUtils.getShortestConstructor(componentClass);
        if (constructor == null) {
            return;
        }
        // set default creation
        String source = getDefaultConstructorInvocation(constructor);
        CreationDescription creationDefault = new CreationDescription(componentDescription, null, null);
        creationDefault.setSource(source);
        componentDescription.setCreationDefault(creationDefault);
    }

    /**
     * TODO move into {@link ReflectionUtils}.
     * 
     * @return the source for creating {@link Object} using given {@link Constructor} with values
     *         default for type of each argument.
     */
    public static String getDefaultConstructorInvocation(Constructor<?> constructor) {
        // prepare Class
        Class<?> componentClass = constructor.getDeclaringClass();
        String componentClassName = ReflectionUtils.getCanonicalName(componentClass);
        // prepare arguments
        String arguments;
        {
            StringBuilder buffer = new StringBuilder();
            for (Class<?> parameter : constructor.getParameterTypes()) {
                String parameterName = ReflectionUtils.getCanonicalName(parameter);
                buffer.append(AstParser.getDefaultValue(parameterName));
                buffer.append(", ");
            }
            arguments = StringUtils.removeEnd(buffer.toString(), ", ");
        }
        // prepare source
        return "new " + componentClassName + "(" + arguments + ")";
    }

    /**
     * Sets icon for {@link ComponentDescription}.
     * 
     * @param context
     *          the {@link EditorState} to access environment.
     * @param componentDescription
     *          the {@link ComponentDescription} to set icon for.
     * @param currentClass
     *          the {@link Class} to check for icon.
     */
    private static void setIcon(ILoadingContext context, ComponentDescription componentDescription,
            Class<?> currentClass) throws Exception {
        if (currentClass != null) {
            // check current Class
            if (componentDescription.getIcon() == null) {
                Image icon = DescriptionHelper.getIconImage(context, currentClass);
                if (icon != null) {
                    componentDescription.setIcon(icon);
                    {
                        String name = componentDescription.getComponentClass().getName();
                        ImageDisposer.add(componentDescription, name, icon);
                    }
                    return;
                }
            }
            // check interfaces
            for (Class<?> interfaceClass : currentClass.getInterfaces()) {
                if (componentDescription.getIcon() == null) {
                    setIcon(context, componentDescription, interfaceClass);
                }
            }
            // check super Class
            if (componentDescription.getIcon() == null) {
                setIcon(context, componentDescription, currentClass.getSuperclass());
            }
        }
    }

    /**
     * Ensures that {@link AbstractInvocationDescription} is fully initialized. See
     * {@link AbstractInvocationDescription#setInitialized(boolean)} for more information.
     */
    public static void ensureInitialized(final IJavaProject javaProject,
            final AbstractInvocationDescription methodDescription) {
        if (!methodDescription.isInitialized()) {
            methodDescription.setInitialized(true);
            // do initialize
            ExecutionUtils.runIgnore(new RunnableEx() {
                public void run() throws Exception {
                    IMethod method = CodeUtils.findMethod(javaProject,
                            methodDescription.getDeclaringClass().getName(), methodDescription.getSignature());
                    if (method != null) {
                        String[] parameterNames = method.getParameterNames();
                        for (ParameterDescription parameter : methodDescription.getParameters()) {
                            if (parameter.getName() == null) {
                                int parameterIndex = parameter.getIndex();
                                String parameterName = parameterNames[parameterIndex];
                                parameter.setName(parameterName);
                            }
                        }
                    }
                }
            });
        }
    }

    /**
     * Adds {@link ConstructorDescription} for given {@link ComponentDescription}.
     */
    private static void addConstructors(IJavaProject javaProject, ComponentDescription componentDescription)
            throws Exception {
        Class<?> componentClass = componentDescription.getComponentClass();
        for (Constructor<?> constructor : componentClass.getDeclaredConstructors()) {
            constructor.setAccessible(true);
            ConstructorDescription constructorDescription = new ConstructorDescription(componentClass);
            // add parameter descriptions of constructor
            for (Class<?> parameterType : constructor.getParameterTypes()) {
                addParameter(constructorDescription, parameterType);
            }
            // OK, add constructor description
            constructorDescription.postProcess();
            componentDescription.addConstructor(constructorDescription);
        }
    }

    private static void addParameter(AbstractInvocationDescription description, Class<?> parameterType)
            throws Exception {
        ParameterDescription parameterDescription = new ParameterDescription();
        parameterDescription.setType(parameterType);
        description.addParameter(parameterDescription);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Rules
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Adds {@link Rule}'s required for component description parsing.
     */
    private static void addRules(Digester digester, AstEditor editor, Class<?> componentClass) {
        EditorState state = EditorState.get(editor);
        ILoadingContext context = EditorStateLoadingContext.get(state);
        digester.addRule("component/toolkit", new ToolkitRule());
        digester.addRule("component/model", new ModelClassRule());
        // component order
        {
            String pattern = "component/order";
            digester.addCallMethod(pattern, "setOrder", 1);
            digester.addCallParam(pattern, 0);
        }
        // standard bean properties
        {
            digester.addRule("component/standard-bean-properties", new StandardBeanPropertiesRule());
            digester.addRule("component/properties-preferred", new StandardBeanPropertiesPreferredRule());
            digester.addRule("component/properties-normal", new StandardBeanPropertiesNormalRule());
            digester.addRule("component/properties-advanced", new StandardBeanPropertiesAdvancedRule());
            digester.addRule("component/properties-hidden", new StandardBeanPropertiesHiddenRule());
            digester.addRule("component/properties-noDefaultValue", new StandardBeanPropertiesNoDefaultValueRule());
            digester.addRule("component/property-tag", new StandardBeanPropertyTagRule());
            {
                String pattern = "component/method-single-property";
                digester.addRule(pattern, new MethodSinglePropertyRule());
                addPropertyConfigurationRules(digester, state, pattern);
            }
            digester.addRule("component/method-property", new MethodPropertyRule(editor.getJavaProject()));
        }
        // public field properties
        {
            digester.addRule("component/public-field-properties", new PublicFieldPropertiesRule());
        }
        // creations
        {
            addCreationRules(digester, context, "component/creation", "addCreation");
            digester.addRule("component/creation/tag", new CreationTagRule());
            digester.addRule("component/creation/typeParameters/typeParameter", new CreationTypeParametersRule());
            addCreationRules(digester, context, "component/creation-default", "setCreationDefault");
            digester.addRule("component/creation-default/tag", new CreationTagRule());
        }
        // morphing targets
        {
            String pattern = "component/morphTargets/morphTarget";
            digester.addRule(pattern, new MorphingTargetRule(state));
        }
        {
            String pattern = "component/morphTargets/noInherit";
            digester.addRule(pattern, new MorphingNoInheritRule());
        }
        // description text
        {
            String pattern = "component/description";
            digester.addCallMethod(pattern, "setDescription", 1);
            digester.addCallParam(pattern, 0);
        }
        // constructors
        {
            String pattern = "component/constructors/constructor";
            digester.addRule(pattern, new ConstructorRule());
            digester.addSetProperties(pattern);
            addParametersRules(digester, pattern + "/parameter", state);
        }
        // methods
        {
            String pattern = "component/methods/method";
            digester.addRule(pattern, new MethodRule());
            digester.addRule(pattern,
                    new SetListedPropertiesRule(new String[] { "order" }, new String[] { "orderSpecification" }));
            digester.addRule(pattern + "/tag", new MethodTagRule());
            addParametersRules(digester, pattern + "/parameter", state);
        }
        // method order
        {
            String pattern = "component/method-order";
            digester.addRule(pattern + "/default", new MethodOrderDefaultRule());
            digester.addRule(pattern + "/method", new MethodOrderMethodRule());
            digester.addRule(pattern + "/methods", new MethodOrderMethodsRule());
            digester.addRule(pattern + "/methods/s", new MethodOrderMethodsSignatureRule());
        }
        // exposed children
        {
            String pattern = "component/exposing-rules";
            digester.addRule(pattern + "/include", new ExposingRulesRule());
            digester.addRule(pattern + "/exclude", new ExposingRulesRule());
        }
        // methods-exclude, methods-include
        {
            digester.addRule("component/methods/methods-include", new MethodsOperationRule(true));
            digester.addRule("component/methods/methods-exclude", new MethodsOperationRule(false));
        }
        // untyped parameters
        {
            String pattern = "component/parameters/parameter";
            digester.addCallMethod(pattern, "addParameter", 2);
            digester.addCallParam(pattern, 0, "name");
            digester.addCallParam(pattern, 1);
        }
        addPropertiesRules(digester, state);
        addConfigurablePropertiesRules(digester, state);
    }

    /**
     * Adds {@link Rule}'s for changing {@link GenericPropertyDescription}'s.
     */
    private static void addPropertiesRules(Digester digester, EditorState state) {
        String propertyAccessPattern = "component/property";
        digester.addRule(propertyAccessPattern, new PropertyAccessRule());
        addPropertyConfigurationRules(digester, state, propertyAccessPattern);
    }

    /**
     * Adds {@link Rule}'s for configuring {@link GenericPropertyDescription} on stack.
     */
    private static void addPropertyConfigurationRules(Digester digester, EditorState state,
            String propertyAccessPattern) {
        // category
        {
            String pattern = propertyAccessPattern + "/category";
            digester.addRule(pattern, new PropertyCategoryRule());
        }
        // editor
        {
            String pattern = propertyAccessPattern + "/editor";
            digester.addRule(pattern, new PropertyEditorRule(state));
            addConfigurableObjectParametersRules(digester, pattern);
        }
        // defaultValue
        {
            String pattern = propertyAccessPattern + "/defaultValue";
            ClassLoader classLoader = state.getEditorLoader();
            digester.addRule(pattern, new PropertyDefaultRule(classLoader));
        }
        // getter
        {
            String pattern = propertyAccessPattern + "/getter";
            digester.addRule(pattern, new PropertyGetterRule());
        }
        // tag
        {
            String pattern = propertyAccessPattern + "/tag";
            digester.addRule(pattern, new PropertyTagRule());
        }
    }

    /**
     * Adds {@link Rule}'s for adding {@link ConfigurablePropertyDescription}'s.
     */
    private static void addConfigurablePropertiesRules(Digester digester, EditorState state) {
        String pattern = "component/add-property";
        digester.addRule(pattern, new ConfigurablePropertyRule());
        addConfigurableObjectParametersRules(digester, pattern);
    }

    /**
     * Adds {@link Rule}'s for configuring {@link AbstractConfigurableDescription}.
     */
    private static void addConfigurableObjectParametersRules(Digester digester, String pattern) {
        digester.addRule(pattern + "/parameter", new ConfigurableObjectParameterRule());
        digester.addRule(pattern + "/parameter-list", new ConfigurableObjectListParameterRule());
    }

    /**
     * Adds {@link Rule}'s for parsing {@link CreationDescription}'s.
     */
    private static void addCreationRules(Digester digester, final ILoadingContext context, String basePattern,
            String setCreationMethod) {
        digester.addFactoryCreate(basePattern, new AbstractObjectCreationFactory() {
            @Override
            public Object createObject(Attributes attributes) throws Exception {
                ComponentDescription componentDescription = (ComponentDescription) getDigester().peek();
                // prepare creation
                String id = attributes.getValue("id");
                String name = attributes.getValue("name");
                CreationDescription creation = new CreationDescription(componentDescription, id, name);
                // set optional specific icon
                if (id != null) {
                    Class<?> componentClass = componentDescription.getComponentClass();
                    String suffix = "_" + id;
                    creation.setIcon(DescriptionHelper.getIconImage(context, componentClass, suffix));
                }
                // OK, configured creation
                return creation;
            }
        });
        digester.addSetNext(basePattern, setCreationMethod);
        // description
        {
            String pattern = basePattern + "/description";
            digester.addCallMethod(pattern, "setDescription", 1);
            digester.addCallParam(pattern, 0);
        }
        // source
        {
            String pattern = basePattern + "/source";
            digester.addCallMethod(pattern, "setSource", 1);
            digester.addCallParam(pattern, 0);
        }
        // invocation
        {
            String pattern = basePattern + "/invocation";
            digester.addRule(pattern, new ObjectCreateRule(CreationInvocationDescription.class));
            digester.addRule(pattern, new SetListedPropertiesRule(new String[] { "signature" }));
            // arguments
            digester.addCallMethod(pattern, "setArguments", 1);
            digester.addCallParam(pattern, 0);
            // add
            digester.addSetNext(pattern, "addInvocation");
        }
        // untyped parameters
        {
            String pattern = basePattern + "/parameter";
            digester.addCallMethod(pattern, "addParameter", 2);
            digester.addCallParam(pattern, 0, "name");
            digester.addCallParam(pattern, 1);
        }
    }

    /**
     * Adds {@link Rule}'s for parsing {@link ParameterDescription}'s.
     */
    static void addParametersRules(Digester digester, String pattern, EditorState state) {
        ClassLoader classLoader = state.getEditorLoader();
        //
        digester.addRule(pattern, new ObjectCreateRule(ParameterDescription.class));
        digester.addRule(pattern, new SetClassPropertyRule(classLoader, "type"));
        digester.addRule(pattern, new SetListedPropertiesRule(
                new String[] { "name", "defaultSource", "parent", "child", "property", "parent2", "child2" }));
        digester.addSetNext(pattern, "addParameter");
        // editors
        {
            String editorPattern = pattern + "/editor";
            digester.addRule(editorPattern, new ParameterEditorRule(state));
            addConfigurableObjectParametersRules(digester, editorPattern);
        }
        // tags
        digester.addRule(pattern + "/tag", new ParameterTagRule());
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Utils
    //
    ////////////////////////////////////////////////////////////////////////////
    private static boolean shouldCacheDescriptions_inPackage(ClassResourceInfo descriptionInfo,
            Class<?> componentClass) throws Exception {
        return hasMarkerFileForPackage(descriptionInfo, componentClass, ".wbp-cache-descriptions");
    }

    private static boolean shouldCachePresentation(ClassResourceInfo descriptionInfo, Class<?> componentClass)
            throws Exception {
        if (descriptionInfo.clazz == componentClass) {
            Bundle bundle = descriptionInfo.resource.getBundle();
            if (bundle != null) {
                return bundle.getEntry("wbp-meta/.wbp-cache-presentations") != null;
            }
        }
        return false;
    }

    /**
     * Checks if package of given class has marker file with given name.
     * 
     * @param descriptionInfo
     *          the first {@link ClassResourceInfo} for <code>*.wbp-component.xml</code>, in
     *          "to superclass" direction. We look into its {@link Bundle} for caching marker.
     * 
     * @return <code>true</code> if package with given class has "cache enabled" marker.
     */
    private static boolean hasMarkerFileForPackage(ClassResourceInfo descriptionInfo, Class<?> componentClass,
            String markerFileName) throws Exception {
        ResourceInfo resourceInfo = descriptionInfo.resource;
        if (resourceInfo.getBundle() != null) {
            String packageName = CodeUtils.getPackage(componentClass.getName());
            String markerName = packageName.replace('.', '/') + "/" + markerFileName;
            return DescriptionHelper.getResourceInfo(null, resourceInfo.getBundle(), markerName) != null;
        }
        return false;
    }

    /**
     * @return the instances of {@link IDescriptionProcessor}.
     */
    public static List<IDescriptionProcessor> getDescriptionProcessors() {
        return ExternalFactoriesHelper.getElementsInstances(IDescriptionProcessor.class,
                "org.eclipse.wb.core.descriptionProcessors", "processor");
    }
}