com.google.gdt.eclipse.designer.uibinder.model.util.NameSupport.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gdt.eclipse.designer.uibinder.model.util.NameSupport.java

Source

/*******************************************************************************
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * 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
 *
 * 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 com.google.gdt.eclipse.designer.uibinder.model.util;

import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gdt.eclipse.designer.GwtToolkitDescription;
import com.google.gdt.eclipse.designer.model.widgets.support.IDevModeBridge;
import com.google.gdt.eclipse.designer.uibinder.IExceptionConstants;
import com.google.gdt.eclipse.designer.uibinder.model.widgets.UIObjectInfo;
import com.google.gdt.eclipse.designer.uibinder.parser.UiBinderContext;
import com.google.gdt.eclipse.designer.uibinder.parser.UiBinderParser;
import com.google.gdt.eclipse.designer.util.Utils;

import org.eclipse.wb.core.model.ObjectInfo;
import org.eclipse.wb.core.model.broadcast.ObjectInfoDelete;
import org.eclipse.wb.core.model.broadcast.ObjectInfoPresentationDecorateText;
import org.eclipse.wb.internal.core.DesignerPlugin;
import org.eclipse.wb.internal.core.model.ObjectInfoVisitor;
import org.eclipse.wb.internal.core.model.description.CreationDescription;
import org.eclipse.wb.internal.core.model.description.CreationDescription.TypeParameterDescription;
import org.eclipse.wb.internal.core.model.description.helpers.ComponentDescriptionHelper;
import org.eclipse.wb.internal.core.model.variable.NamesManager;
import org.eclipse.wb.internal.core.model.variable.NamesManager.ComponentNameDescription;
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.BodyDeclarationTarget;
import org.eclipse.wb.internal.core.utils.ast.DomGenerics;
import org.eclipse.wb.internal.core.utils.exception.DesignerException;
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.execution.RunnableObjectEx;
import org.eclipse.wb.internal.core.utils.jdt.core.CodeUtils;
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.xml.DocumentElement;
import org.eclipse.wb.internal.core.xml.model.XmlObjectInfo;
import org.eclipse.wb.internal.core.xml.model.broadcast.XmlObjectAdd;
import org.eclipse.wb.internal.core.xml.model.description.ComponentDescription;
import org.eclipse.wb.internal.core.xml.model.utils.XmlObjectUtils;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IExtendedModifier;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.ui.refactoring.RenameSupport;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Support for "ui:field" attribute and "@UiField" in Java.
 * 
 * @author scheglov_ke
 * @coverage GWT.UiBinder.model
 */
public final class NameSupport {
    ////////////////////////////////////////////////////////////////////////////
    //
    // Static utils
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Sometimes widget has no text, so it is hard to identify in components tree. But if it has name,
     * would be nice to show it.
     */
    public static void decoratePresentationWithName(XmlObjectInfo root) {
        root.addBroadcastListener(new ObjectInfoPresentationDecorateText() {
            public void invoke(ObjectInfo object, String[] text) throws Exception {
                if (object instanceof XmlObjectInfo) {
                    XmlObjectInfo xObject = (XmlObjectInfo) object;
                    String name = getName(xObject);
                    if (name != null) {
                        text[0] = text[0] + " - " + name;
                    }
                }
            }
        });
    }

    /**
     * Deletes artifacts of widget from Java source.
     */
    public static void removeName_onDelete(XmlObjectInfo rootObject) {
        rootObject.addBroadcastListener(new ObjectInfoDelete() {
            @Override
            public void before(ObjectInfo parent, ObjectInfo child) throws Exception {
                if (child instanceof XmlObjectInfo) {
                    XmlObjectInfo object = (XmlObjectInfo) child;
                    removeName(object);
                }
            }
        });
    }

    /**
     * If widget marked with <code>"UiBinder.createFieldProvided"</code> then during creation ensure
     * <code>@UiField(provided=true)</code> using default Java creation.
     */
    public static void ensureFieldProvided_onCreate(XmlObjectInfo rootObject) {
        rootObject.addBroadcastListener(new XmlObjectAdd() {
            @Override
            public void after(ObjectInfo parent, XmlObjectInfo child) throws Exception {
                if (XmlObjectUtils.hasTrueParameter(child, "UiBinder.createFieldProvided")) {
                    NameSupport nameSupport = new NameSupport(child);
                    nameSupport.createFieldProvided();
                }
            }
        });
    }

    /**
     * @return the existing name of the widget, or <code>null</code>.
     */
    public static String getName(XmlObjectInfo object) {
        if (XmlObjectUtils.isImplicit(object)) {
            return null;
        }
        return new NameSupport(object).getName();
    }

    /**
     * @return the existing or new name of the widget, can not be <code>null</code>.
     */
    public static String ensureName(XmlObjectInfo object) throws Exception {
        if (XmlObjectUtils.isImplicit(object)) {
            throw new IllegalArgumentException();
        }
        final NameSupport nameSupport = new NameSupport(object);
        return ExecutionUtils.runObject(object, new RunnableObjectEx<String>() {
            public String runObject() throws Exception {
                return nameSupport.ensureName();
            }
        });
    }

    /**
     * Removes "@UiField" of the widget.
     */
    public static void removeName(XmlObjectInfo object) throws Exception {
        if (XmlObjectUtils.isImplicit(object)) {
            return;
        }
        final NameSupport nameSupport = new NameSupport(object);
        ExecutionUtils.run(object, new RunnableEx() {
            public void run() throws Exception {
                nameSupport.removeName();
            }
        });
    }

    /**
     * Sets new name for "@UiField" of the widget.
     */
    public static void setName(XmlObjectInfo object, final String name) throws Exception {
        if (XmlObjectUtils.isImplicit(object)) {
            throw new IllegalArgumentException();
        }
        m_renaming = true;
        try {
            final NameSupport nameSupport = new NameSupport(object);
            ExecutionUtils.run(object, new RunnableEx() {
                public void run() throws Exception {
                    nameSupport.setName(name);
                }
            });
        } finally {
            m_renaming = false;
        }
    }

    /**
     * @return the error message if given name is not valid, or <code>null</code> if this name can be
     *         used.
     */
    public static String validateName(XmlObjectInfo object, String name) throws Exception {
        return new NameSupport(object).validateName(name);
    }

    /**
     * @return the {@link XmlObjectInfo} with the given name, may be <code>null</code>.
     */
    public static XmlObjectInfo getObject(XmlObjectInfo root, final String name) {
        final XmlObjectInfo result[] = { null };
        root.accept(new ObjectInfoVisitor() {
            @Override
            public void endVisit(ObjectInfo object) throws Exception {
                if (object instanceof XmlObjectInfo) {
                    XmlObjectInfo xmlObject = (XmlObjectInfo) object;
                    if (ObjectUtils.equals(getName(xmlObject), name)) {
                        result[0] = xmlObject;
                    }
                }
            }
        });
        return result[0];
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Instance fields
    //
    ////////////////////////////////////////////////////////////////////////////
    private final XmlObjectInfo m_object;
    private final UiBinderContext m_context;

    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructor
    //
    ////////////////////////////////////////////////////////////////////////////
    private NameSupport(XmlObjectInfo object) {
        m_object = object;
        m_context = (UiBinderContext) m_object.getContext();
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Name
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the existing name of the widget, or <code>null</code>.
     */
    public String getName() {
        String nameAttribute = getNameAttribute();
        return m_object.getAttribute(nameAttribute);
    }

    /**
     * @return the existing or new name of the widget, can not be <code>null</code>.
     */
    public String ensureName() throws Exception {
        String name = getName();
        if (name == null) {
            name = generateName();
            setName(name);
        }
        return name;
    }

    /**
     * Removes "@UiField" of the widget.
     */
    private void removeName() throws Exception {
        String name = getName();
        if (name != null) {
            // remove attribute
            {
                String nameAttribute = getNameAttribute();
                m_object.getElement().setAttribute(nameAttribute, null);
            }
            // update Java
            {
                AstEditor editor = getEditor();
                TypeDeclaration typeDeclaration = editor.getPrimaryType();
                // remove handlers
                for (MethodDeclaration methodDeclaration : typeDeclaration.getMethods()) {
                    if (EventHandlerProperty.isObjectHandler(methodDeclaration, name)) {
                        editor.removeBodyDeclaration(methodDeclaration);
                    }
                }
                // remove field
                VariableDeclaration variable = getBinderField(typeDeclaration, name);
                FieldDeclaration field = AstNodeUtils.getEnclosingFieldDeclaration(variable);
                editor.removeBodyDeclaration(field);
            }
        }
    }

    /**
     * Sets new name for "@UiField" of the widget.
     */
    private void setName(String name) throws Exception {
        AstEditor editor = getEditor();
        TypeDeclaration typeDeclaration = editor.getPrimaryType();
        // prepare "old" state
        String oldName = getName();
        VariableDeclaration oldVariable = getBinderField(typeDeclaration, oldName);
        // set "ui:field" attribute
        {
            String nameAttribute = getNameAttribute();
            m_object.setAttribute(nameAttribute, name);
        }
        // update Java
        if (oldVariable != null) {
            IType modelType = m_context.getFormType();
            IField modelField = modelType.getField(oldName);
            RenameSupport renameSupport = RenameSupport.create(modelField, name, RenameSupport.UPDATE_REFERENCES);
            renameSupport.perform(DesignerPlugin.getShell(), DesignerPlugin.getActiveWorkbenchWindow());
            return;
        } else {
            Class<?> componentClass = m_object.getDescription().getComponentClass();
            String source = "@com.google.gwt.uibinder.client.UiField "
                    + ReflectionUtils.getCanonicalName(componentClass) + " " + name + ";";
            BodyDeclarationTarget target = getNewFieldTarget(typeDeclaration);
            editor.addFieldDeclaration(source, target);
        }
    }

    /**
     * @return the error message if given name is not valid, or <code>null</code> if this name can be
     *         used.
     */
    private String validateName(String name) throws Exception {
        // check that identifier is valid
        {
            IJavaProject javaProject = m_object.getContext().getJavaProject();
            String sourceLevel = javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
            String complianceLevel = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
            IStatus status = JavaConventions.validateFieldName(name, sourceLevel, complianceLevel);
            if (status.matches(IStatus.ERROR)) {
                return status.getMessage();
            }
        }
        // check that name is unique
        {
            Set<String> existingNames = getExistingNames();
            if (existingNames.contains(name)) {
                return "Field '" + name + "' already exists.";
            }
        }
        // OK
        return null;
    }

    /**
     * Creates <code>@UiField(provided=true)</code> using default Java creation.
     */
    private void createFieldProvided() throws Exception {
        // may be no support for @UiField
        if (!UiBinderParser.hasUiFieldUiFactorySupport(m_context.getJavaProject())) {
            throw new DesignerException(IExceptionConstants.UI_FIELD_FACTORY_FEATURE);
        }
        // prepare AST
        AstEditor editor = getEditor();
        // configure EditorState
        EditorState.get(editor).initialize(GwtToolkitDescription.INSTANCE.getId(),
                m_object.getContext().getClassLoader());
        // prepare name
        String name = generateName();
        // set "ui:field" attribute
        {
            String nameAttribute = getNameAttribute();
            m_object.setAttribute(nameAttribute, name);
        }
        // prepare CreationDescription for Java
        Class<?> componentClass = m_object.getDescription().getComponentClass();
        org.eclipse.wb.internal.core.model.description.ComponentDescription javaDescription = ComponentDescriptionHelper
                .getDescription(editor, componentClass);
        CreationDescription creationDescription = javaDescription.getCreation(null);
        // update Java
        String fieldTypeName = ReflectionUtils.getCanonicalName(componentClass);
        String creationSource = creationDescription.getSource();
        // apply generics
        {
            Set<Entry<String, TypeParameterDescription>> entrySet = creationDescription.getTypeParameters()
                    .entrySet();
            if (!entrySet.isEmpty()) {
                fieldTypeName += "<";
                for (Entry<String, TypeParameterDescription> entry : entrySet) {
                    // use type bounds, until we have dialog to ask type arguments
                    String typeArgument = entry.getValue().getTypeName();
                    // field type
                    if (!fieldTypeName.endsWith("<")) {
                        fieldTypeName += ", ";
                    }
                    fieldTypeName += typeArgument;
                    // creation source
                    creationSource = StringUtils.replace(creationSource, "%" + entry.getKey() + "%", typeArgument);
                }
                fieldTypeName += ">";
            }
        }
        addUiFieldJava(componentClass, fieldTypeName, name, creationSource);
    }

    /**
     * Adds @UiField with given type, name and initializer.
     */
    public void addUiFieldJava(Class<?> fieldClass, String fieldTypeName, String name, String initializer)
            throws Exception {
        AstEditor editor = getEditor();
        TypeDeclaration typeDeclaration = editor.getPrimaryType();
        // prepare source lines
        List<String> lines;
        {
            lines = Lists.newArrayList();
            String source = "@com.google.gwt.uibinder.client.UiField(provided=true) ";
            source += fieldTypeName + " " + name + " = " + initializer + ";";
            Collections.addAll(lines, StringUtils.split(source, '\n'));
        }
        // add field
        BodyDeclarationTarget target = getNewFieldTarget(typeDeclaration);
        editor.addFieldDeclaration(lines, target);
        // add new JField into "form" JType
        addFormJField(fieldClass, name);
    }

    /**
     * Adds <code>JField</code> with given name and @UiField annotation into "form" <code>JType</code>
     * .
     */
    private void addFormJField(Class<?> componentClass, String name) throws Exception {
        IDevModeBridge bridge = m_context.getState().getDevModeBridge();
        ClassLoader devClassLoader = bridge.getDevClassLoader();
        // prepare "form" JType
        Object formType = bridge.findJType(m_context.getFormType().getFullyQualifiedName());
        // prepare @UiField annotation instance
        Class<?> uiFieldAnnotationClass = devClassLoader.loadClass("com.google.gwt.uibinder.client.UiField");
        java.lang.annotation.Annotation annotation = (java.lang.annotation.Annotation) Proxy.newProxyInstance(
                uiFieldAnnotationClass.getClassLoader(), new Class[] { uiFieldAnnotationClass },
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) {
                        return Boolean.TRUE;
                    }
                });
        // add new JField
        Object newField;
        {
            Constructor<?> fieldConstructor;
            Class<?> fieldClass;
            if (m_context.getState().getVersion().isHigherOrSame(Utils.GWT_2_2)) {
                fieldClass = devClassLoader.loadClass("com.google.gwt.dev.javac.typemodel.JField");
                fieldConstructor = ReflectionUtils.getConstructorBySignature(fieldClass,
                        "<init>(com.google.gwt.dev.javac.typemodel.JClassType,java.lang.String,java.util.Map)");
            } else {
                fieldClass = devClassLoader.loadClass("com.google.gwt.core.ext.typeinfo.JField");
                fieldConstructor = ReflectionUtils.getConstructorBySignature(fieldClass,
                        "<init>(com.google.gwt.core.ext.typeinfo.JClassType,java.lang.String,java.util.Map)");
            }
            newField = fieldConstructor.newInstance(formType, name,
                    ImmutableMap.of(uiFieldAnnotationClass, annotation));
        }
        // set "widget" JType for JField
        Object widgetType = bridge.findJType(ReflectionUtils.getCanonicalName(componentClass));
        ReflectionUtils.invokeMethod(newField, "setType(com.google.gwt.core.ext.typeinfo.JType)", widgetType);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Temporary state
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * When we change name ourself, using "UiField" property, we don't want to change
     * <code>*.ui.xml</code> file, because this causes reparsing and all related problems. So, we
     * disable template changing temporary.
     */
    private static boolean m_renaming;

    /**
     * @return the <code>true</code> if <code>*.ui.xml</code> is changing by {@link NameSupport} now.
     */
    public static boolean isRenaming() {
        return m_renaming;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Field utils
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the {@link AstEditor} for companion Java unit.
     */
    private AstEditor getEditor() throws Exception {
        return m_context.getFormEditor();
    }

    /**
     * @return the {@link VariableDeclaration} of "@UiField" with given name, may be <code>null</code>
     *         .
     */
    public static VariableDeclaration getBinderField(TypeDeclaration typeDeclaration, final String name) {
        final VariableDeclaration[] result = { null };
        typeDeclaration.accept(new ASTVisitor() {
            @Override
            public void endVisit(FieldDeclaration node) {
                if (isBinderField(node)) {
                    for (VariableDeclaration fragment : DomGenerics.fragments(node)) {
                        if (fragment.getName().getIdentifier().equals(name)) {
                            result[0] = fragment;
                        }
                    }
                }
            }
        });
        return result[0];
    }

    /**
     * @return the {@link VariableDeclaration}s of with "@UiField(provided)".
     */
    public static List<FieldDeclaration> getUiFields(TypeDeclaration typeDeclaration) {
        final List<FieldDeclaration> fields = Lists.newArrayList();
        typeDeclaration.accept(new ASTVisitor() {
            @Override
            public void endVisit(FieldDeclaration node) {
                if (isBinderField(node)) {
                    fields.add(node);
                }
            }
        });
        return fields;
    }

    /**
     * Adds @UiField with given type, name and initializer.
     */
    public static String addUiFieldJava(UIObjectInfo contextObject, Class<?> fieldClass, String fieldTypeName,
            String baseName, String initializer) throws Exception {
        NameSupport nameSupport = new NameSupport(contextObject);
        String name = nameSupport.generateName(baseName);
        nameSupport.addUiFieldJava(fieldClass, fieldTypeName, name, initializer);
        return name;
    }

    /**
     * @return the {@link BodyDeclarationTarget} to add new "@UiField".
     */
    private static BodyDeclarationTarget getNewFieldTarget(TypeDeclaration typeDeclaration) {
        final FieldDeclaration[] lastUiField = { null };
        final FieldDeclaration[] binderCreate = { null };
        typeDeclaration.accept(new ASTVisitor() {
            @Override
            public void endVisit(FieldDeclaration node) {
                if (isBinderField(node)) {
                    lastUiField[0] = node;
                }
                if (isBinderCreate(node)) {
                    binderCreate[0] = node;
                }
            }
        });
        // after last @UiField
        if (lastUiField[0] != null) {
            return new BodyDeclarationTarget(lastUiField[0], false);
        }
        // after GWT.create(Binder.class)
        if (binderCreate[0] != null) {
            return new BodyDeclarationTarget(binderCreate[0], false);
        }
        // at type end
        return new BodyDeclarationTarget(typeDeclaration, false);
    }

    /**
     * @return <code>true</code> if given {@link FieldDeclaration} is "@UiField".
     */
    public static boolean isBinderField(FieldDeclaration fieldDeclaration) {
        for (IExtendedModifier modifier : DomGenerics.modifiers(fieldDeclaration)) {
            if (modifier instanceof Annotation) {
                Annotation annotation = (Annotation) modifier;
                if (isBinderAnnotation(annotation)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @return <code>true</code> if given {@link Annotation} is "@UiField".
     */
    public static boolean isBinderAnnotation(Annotation annotation) {
        return AstNodeUtils.isSuccessorOf(annotation, "com.google.gwt.uibinder.client.UiField");
    }

    /**
     * @return <code>true</code> if has initializer <code>GWT.create(UiBinder+.class)</code>.
     */
    public static boolean isBinderCreate(FieldDeclaration fieldDeclaration) {
        List<VariableDeclarationFragment> fragments = DomGenerics.fragments(fieldDeclaration);
        for (VariableDeclaration fragment : fragments) {
            if (fragment.getInitializer() instanceof MethodInvocation) {
                MethodInvocation invocation = (MethodInvocation) fragment.getInitializer();
                if (invocation.getName().getIdentifier().equals("create") && AstNodeUtils
                        .isSuccessorOf(invocation.getExpression(), "com.google.gwt.core.client.GWT")) {
                    List<Expression> arguments = DomGenerics.arguments(invocation);
                    if (arguments.size() == 1 && arguments.get(0) instanceof TypeLiteral) {
                        TypeLiteral typeLiteral = (TypeLiteral) arguments.get(0);
                        if (AstNodeUtils.isSuccessorOf(typeLiteral.getType(),
                                "com.google.gwt.uibinder.client.UiBinder")) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Name utils
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the full name (including namespace) for "field" attribute.
     */
    private String getNameAttribute() {
        DocumentElement rootElement = m_object.getElement().getRoot();
        return rootElement.getTagNS() + "field";
    }

    /**
     * @return the generated unique name basing on settings and info in *.wbp-component.xml
     *         description.
     */
    private String generateName() throws Exception {
        String baseName = getBaseName();
        return generateName(baseName);
    }

    /**
     * @return the generated unique name based on the given.
     */
    private String generateName(String baseName) throws Exception {
        final Set<String> identifiers = getExistingNames();
        String uniqueName = CodeUtils.generateUniqueName(baseName, new Predicate<String>() {
            public boolean apply(String name) {
                return !identifiers.contains(name);
            }
        });
        return uniqueName;
    }

    /**
     * Traverses the entire hierarchy and gathers set of existing names.
     */
    private Set<String> getExistingNames() throws Exception {
        final Set<String> resultSet = Sets.newTreeSet();
        // add ui:field
        m_object.getRootXML().accept(new ObjectInfoVisitor() {
            @Override
            public void endVisit(ObjectInfo object) throws Exception {
                if (object instanceof XmlObjectInfo) {
                    XmlObjectInfo xmlObject = (XmlObjectInfo) object;
                    if (!XmlObjectUtils.isImplicit(xmlObject)) {
                        String name = getName(xmlObject);
                        if (name != null) {
                            resultSet.add(name);
                        }
                    }
                }
            }
        });
        // add TypeDeclaration fields
        {
            AstEditor editor = getEditor();
            TypeDeclaration typeDeclaration = editor.getPrimaryType();
            for (FieldDeclaration fieldDeclaration : typeDeclaration.getFields()) {
                for (VariableDeclarationFragment fragment : DomGenerics.fragments(fieldDeclaration)) {
                    String name = fragment.getName().getIdentifier();
                    resultSet.add(name);
                }
            }
        }
        // done
        return resultSet;
    }

    /**
     * @return the base variable name for given {@link XmlObjectInfo}.
     */
    private String getBaseName() {
        ComponentDescription description = m_object.getDescription();
        String componentClassName = description.getComponentClass().getName();
        // check type specific information
        {
            ComponentNameDescription nameDescription = NamesManager.getNameDescription(description.getToolkit(),
                    componentClassName);
            if (nameDescription != null) {
                return nameDescription.getName();
            }
        }
        // check component parameter
        {
            String name = XmlObjectUtils.getParameter(m_object, NamesManager.NAME_PARAMETER);
            if (!StringUtils.isEmpty(name)) {
                return name;
            }
        }
        // use default name
        return NamesManager.getDefaultName(componentClassName);
    }
}