org.eclipse.wb.internal.core.parser.JavaInfoParser.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wb.internal.core.parser.JavaInfoParser.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.parser;

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

import org.eclipse.wb.core.eval.ExecutionFlowDescription;
import org.eclipse.wb.core.eval.ExecutionFlowUtils;
import org.eclipse.wb.core.eval.ExecutionFlowUtils.ExecutionFlowFrameVisitor;
import org.eclipse.wb.core.eval.ExecutionFlowUtils.VisitingContext;
import org.eclipse.wb.core.eval.ExecutionFlowUtils2;
import org.eclipse.wb.core.eval.ExpressionValue;
import org.eclipse.wb.core.model.IRootProcessor;
import org.eclipse.wb.core.model.JavaInfo;
import org.eclipse.wb.core.model.ObjectInfo;
import org.eclipse.wb.core.model.association.Association;
import org.eclipse.wb.core.model.association.CompoundAssociation;
import org.eclipse.wb.core.model.association.ConstructorChildAssociation;
import org.eclipse.wb.core.model.association.ConstructorParentAssociation;
import org.eclipse.wb.core.model.association.InvocationChildArrayAssociation;
import org.eclipse.wb.core.model.association.InvocationChildAssociation;
import org.eclipse.wb.core.model.association.InvocationChildEllipsisAssociation;
import org.eclipse.wb.core.model.association.InvocationSecondaryAssociation;
import org.eclipse.wb.core.model.association.RootAssociation;
import org.eclipse.wb.core.model.association.SuperConstructorArgumentAssociation;
import org.eclipse.wb.core.model.broadcast.EvaluationEventListener;
import org.eclipse.wb.core.model.broadcast.ExecutionFlowEnterFrame;
import org.eclipse.wb.core.model.broadcast.JavaEventListener;
import org.eclipse.wb.core.model.broadcast.JavaInfoMethodAssociationOnParse;
import org.eclipse.wb.core.model.broadcast.JavaInfoTreeAlmostComplete;
import org.eclipse.wb.core.model.broadcast.ObjectEventListener;
import org.eclipse.wb.core.model.broadcast.ObjectInfoTreeComplete;
import org.eclipse.wb.internal.core.DesignerPlugin;
import org.eclipse.wb.internal.core.EnvironmentUtils;
import org.eclipse.wb.internal.core.model.JavaInfoEvaluationHelper;
import org.eclipse.wb.internal.core.model.JavaInfoUtils;
import org.eclipse.wb.internal.core.model.description.ConstructorDescription;
import org.eclipse.wb.internal.core.model.description.MethodDescription;
import org.eclipse.wb.internal.core.model.description.ParameterDescription;
import org.eclipse.wb.internal.core.model.description.helpers.FactoryDescriptionHelper;
import org.eclipse.wb.internal.core.model.generation.GenerationSettings;
import org.eclipse.wb.internal.core.model.nonvisual.AbstractArrayObjectInfo;
import org.eclipse.wb.internal.core.model.nonvisual.ArrayObjectInfo;
import org.eclipse.wb.internal.core.model.nonvisual.EllipsisObjectInfo;
import org.eclipse.wb.internal.core.model.nonvisual.NonVisualBeanContainerInfo;
import org.eclipse.wb.internal.core.model.nonvisual.NonVisualBeanInfo;
import org.eclipse.wb.internal.core.model.util.GlobalStateJava;
import org.eclipse.wb.internal.core.model.variable.EmptyVariableSupport;
import org.eclipse.wb.internal.core.model.variable.FieldInitializerVariableSupport;
import org.eclipse.wb.internal.core.model.variable.FieldReuseVariableSupport;
import org.eclipse.wb.internal.core.model.variable.FieldUniqueVariableSupport;
import org.eclipse.wb.internal.core.model.variable.LazyVariableSupportUtils;
import org.eclipse.wb.internal.core.model.variable.LocalReuseVariableSupport;
import org.eclipse.wb.internal.core.model.variable.LocalUniqueVariableSupport;
import org.eclipse.wb.internal.core.model.variable.VariableSupport;
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.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.DesignerExceptionUtils;
import org.eclipse.wb.internal.core.utils.exception.ICoreExceptionConstants;
import org.eclipse.wb.internal.core.utils.exception.NoEntryPointError;
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.external.ExternalFactoriesHelper;
import org.eclipse.wb.internal.core.utils.jdt.core.CodeUtils;
import org.eclipse.wb.internal.core.utils.jdt.core.ProjectUtils;
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.state.GlobalState;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;

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

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Parser converts given model {@link ICompilationUnit} into tree of {@link JavaInfo} objects.
 *
 * @author scheglov_ke
 * @coverage core.model.parser
 */
public final class JavaInfoParser implements IJavaInfoParseResolver {
    /**
     * The key for accessing root {@link JavaInfo} using {@link AstEditor#getGlobalValue(String)}.
     */
    public static final String KEY_ROOT = "KEY_ROOT";
    /**
     * The key for accessing all {@link JavaInfo}'s using {@link AstEditor#getGlobalValue(String)}.
     */
    public static final String KEY_COMPONENTS = "KEY_COMPONENTS";

    ////////////////////////////////////////////////////////////////////////////
    //
    // Access
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Parses given compilation unit and returns single root {@link JavaInfo}.
     */
    public static JavaInfo parse(ICompilationUnit modelUnit) throws Exception {
        DesignerPlugin.installSecurityManager();
        checkJavaVersion(modelUnit);
        final JavaInfoParser parser = new JavaInfoParser(modelUnit);
        return ExecutionUtils.runDesignTime(new RunnableObjectEx<JavaInfo>() {
            public JavaInfo runObject() throws Exception {
                return parser.parse();
            }
        });
    }

    /**
     * Parses given {@link MethodDeclaration} in {@link AstEditor}.
     */
    public static JavaInfo parse(AstEditor editor, MethodDeclaration rootMethod) throws Exception {
        final JavaInfoParser parser = new JavaInfoParser(editor);
        parser.m_editorState.setFlowDescription(new ExecutionFlowDescription(rootMethod));
        return ExecutionUtils.runDesignTime(new RunnableObjectEx<JavaInfo>() {
            public JavaInfo runObject() throws Exception {
                return parser.parseRootMethods();
            }
        });
    }

    /**
     * Checks that Java version used to run Eclipse is compatible with Java version of
     * {@link IJavaProject}.
     */
    private static void checkJavaVersion(ICompilationUnit unit) {
        IJavaProject javaProject = unit.getJavaProject();
        float projectVersion = ProjectUtils.getJavaVersion(javaProject);
        float eclipseVersion = EnvironmentUtils.getJavaVersion();
        if (eclipseVersion < projectVersion) {
            NumberFormat format = new DecimalFormat("#.###", new DecimalFormatSymbols(Locale.ENGLISH));
            String projectVersionString = format.format(projectVersion);
            String eclipseVersionString = format.format(eclipseVersion);
            throw new DesignerException(ICoreExceptionConstants.PARSER_JAVA_VERSION, projectVersionString,
                    eclipseVersionString);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Instance fields
    //
    ////////////////////////////////////////////////////////////////////////////
    private final AstEditor m_editor;
    private final EditorState m_editorState;
    private final JavaInfoResolver m_javaInfoResolver;
    private final EvaluationEventListener m_evaluationListener;
    private final List<IParseFactory> m_parseFactories;
    private TypeDeclaration m_typeDeclaration;
    private JavaInfo m_rootComponent;
    private final List<JavaInfo> m_components;

    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructor
    //
    ////////////////////////////////////////////////////////////////////////////
    private JavaInfoParser(ICompilationUnit modelUnit) throws Exception {
        this(new AstEditor(modelUnit));
    }

    private JavaInfoParser(AstEditor editor) throws Exception {
        m_editor = editor;
        m_editorState = EditorState.get(m_editor);
        m_javaInfoResolver = new JavaInfoResolver(editor);
        m_components = m_javaInfoResolver.getComponents();
        m_evaluationListener = m_editorState.getBroadcast().getListener(EvaluationEventListener.class);
        m_parseFactories = ExternalFactoriesHelper.getElementsInstances(IParseFactory.class,
                "org.eclipse.wb.core.java.parseFactories", "factory");
        m_editor.putGlobalValue(KEY_COMPONENTS, m_components);
        GlobalState.setParsing(true);
        m_editorState.setExecuting(true);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Parsing
    //
    ////////////////////////////////////////////////////////////////////////////
    private ExecutionFlowParseVisitor m_visitor;

    /**
     * Parses compilation unit.
     *
     * @return single root {@link JavaInfo}.
     */
    private JavaInfo parse() throws Exception {
        validateASTEditor();
        // prepare parse context
        {
            ParseRootContext rootContext = prepareParseContext();
            // prepare flow description
            ExecutionFlowDescription flowDescription = rootContext.getFlowDescription();
            addStartMethodsNVO(flowDescription);
            // remember root information
            m_rootComponent = rootContext.getRoot();
            m_editorState.setFlowDescription(flowDescription);
            m_editorState.setTmp_visitingContext(new VisitingContext(false));
            m_editorState.setTmp_Components(m_components);
            // add root
            if (rootContext.getRoot() != null) {
                addJavaInfo(rootContext.getRoot(), null);
            }
            // use IParseContextProcessor-s
            {
                List<IParseContextProcessor> processors = ExternalFactoriesHelper.getElementsInstances(
                        IParseContextProcessor.class, "org.eclipse.wb.core.java.parseContextProcessors",
                        "processor");
                for (IParseContextProcessor processor : processors) {
                    processor.process(m_editor, flowDescription, m_components);
                }
            }
        }
        // parse execution flow
        return parseRootMethods();
    }

    /**
     * Uses {@link IParseValidator}s to validate this {@link AstEditor}.
     */
    private void validateASTEditor() throws Exception {
        List<IParseValidator> validators = ExternalFactoriesHelper.getElementsInstances(IParseValidator.class,
                "org.eclipse.wb.core.java.parseFactories", "validator");
        for (IParseValidator validator : validators) {
            validator.validate(m_editor);
        }
    }

    /**
     * We don't visit NVO {@link MethodDeclaration}-s, so we should add them forcedly.
     */
    private static void addStartMethodsNVO(ExecutionFlowDescription flowDescription) throws Exception {
        // prepare enclosing type
        TypeDeclaration typeDeclaration;
        {
            MethodDeclaration startMethod = flowDescription.getStartMethods().get(0);
            typeDeclaration = (TypeDeclaration) startMethod.getParent();
        }
        // check all methods
        for (MethodDeclaration method : typeDeclaration.getMethods()) {
            NonVisualBeanInfo nonVisualInfo = NonVisualBeanContainerInfo.getNonVisualInfo(method);
            if (nonVisualInfo != null) {
                flowDescription.addStartMethod(method);
            }
        }
    }

    /**
     * @return the single root {@link JavaInfo} for current root methods from {@link EditorState}.
     */
    private JavaInfo parseRootMethods() throws Exception {
        ExecutionFlowParseVisitor parseVisitor = getParseVisitor();
        parseVisitor.m_currentStatement = null;
        try {
            // visit execution flow
            ExecutionFlowUtils.visit(m_editorState.getTmp_visitingContext(), m_editorState.getFlowDescription(),
                    parseVisitor);
            GlobalState.setParsing(false);
            m_editorState.setExecuting(false);
            // now we visited execution flow, so lock ExecutionFlowDescription
            m_editorState.getFlowDescription().lockBinaryFlow();
            // find root
            {
                JavaInfo root = getRoot();
                if (root != null) {
                    m_javaInfoResolver.setRootJavaInfo(root);
                    return root;
                }
            }
            // fail, no root found
            if (m_editor.hasCompilationErrors()) {
                throw new DesignerException(ICoreExceptionConstants.PARSER_NO_ROOT_WHEN_COMPILATION_ERRORS);
            }
            throw new NoEntryPointError(m_editor, m_typeDeclaration);
        } catch (Throwable e) {
            disposeComponentsHierarchy(m_components, true);
            // remember position
            if (parseVisitor.m_currentStatement != null) {
                int position = AstNodeUtils.getSourceBegin(parseVisitor.m_currentStatement);
                DesignerExceptionUtils.setSourcePosition(e, position);
            }
            // re-throw
            throw ReflectionUtils.propagate(e);
        }
    }

    /**
     * In case of exception during parsing we have to dispose all components, because after exception
     * we don't return any model object to caller.
     */
    private static void disposeComponentsHierarchy(List<JavaInfo> components, boolean sendBroadcast)
            throws Exception {
        for (JavaInfo component : components) {
            try {
                component.refresh_dispose();
                if (sendBroadcast) {
                    component.getBroadcastObject().dispose();
                }
            } catch (Throwable e) {
                DesignerPlugin.log(e);
            }
        }
    }

    /**
     * If we created {@link JavaInfo} for some {@link Expression} however it was not connected to
     * hierarchy, then we should remove reference on model, to prevent re-using this value again and
     * again. We should evaluate this {@link Expression} as any other.
     */
    private void clearValuesOfDisconnectedModels(final JavaInfo root) {
        if (m_editorState.isLiveComponent()) {
            return;
        }
        m_editor.getAstUnit().accept(new ASTVisitor() {
            @Override
            public void postVisit(ASTNode node) {
                if (node instanceof Expression) {
                    Expression expression = (Expression) node;
                    ExpressionValue value = ExecutionFlowUtils2.getValue0(expression);
                    if (value != null) {
                        JavaInfo model = (JavaInfo) value.getModel();
                        if (model != null && !root.isItOrParentOf(model)) {
                            ExecutionFlowUtils2.clearPermanentValue(expression);
                        }
                    }
                }
            }
        });
    }

    /**
     * @return the {@link ExecutionFlowParseVisitor} that is used by this {@link JavaInfoParser} for
     *         parsing.
     */
    private ExecutionFlowParseVisitor getParseVisitor() {
        if (m_visitor == null) {
            m_visitor = new ExecutionFlowParseVisitor();
        }
        return m_visitor;
    }

    /**
     * @return the root {@link JavaInfo} for parsed components.
     */
    private JavaInfo getRoot() throws Throwable {
        m_editorState.getBroadcast().getListener(JavaEventListener.class).bindComponents(m_components);
        JavaInfoUtils.bindBinaryComponents(m_components);
        // select root JavaInfo
        List<JavaInfo> rootComponents = getRootComponents();
        if (!rootComponents.isEmpty()) {
            try {
                // use first component as root
                JavaInfo root = rootComponents.get(0);
                GlobalStateJava.activate(root);
                root.setAssociation(new RootAssociation());
                // process root
                callRootProcessors(root);
                root.getBroadcast(JavaInfoTreeAlmostComplete.class).invoke(root, m_components);
                m_editor.putGlobalValue(KEY_ROOT, root);
                root.getBroadcast(ObjectInfoTreeComplete.class).invoke();
                clearValuesOfDisconnectedModels(root);
                // do return
                GenerationSettings.deduce(root);
                return root;
            } finally {
                // We create objects during parsing, so dispose them now.
                disposeComponentsHierarchy(rootComponents, false);
            }
        }
        // no root found
        return null;
    }

    /**
     * Calls {@link IRootProcessor}-s.
     */
    private void callRootProcessors(JavaInfo root) throws Exception {
        List<JavaInfo> components = Lists.newArrayList();
        // fill components with no duplicates
        for (JavaInfo javaInfo : m_components) {
            if (!components.contains(javaInfo)) {
                components.add(javaInfo);
            }
        }
        // prepare processors
        List<IRootProcessor> processors = ExternalFactoriesHelper.getElementsInstances(IRootProcessor.class,
                "org.eclipse.wb.core.java.rootProcessors", "processor");
        //
        for (IRootProcessor processor : processors) {
            processor.process(root, components);
        }
    }

    /**
     * @return the potential root {@link JavaInfo}'s, sorted by the hierarchy size.
     */
    private List<JavaInfo> getRootComponents() throws Exception {
        List<JavaInfo> rootComponents = new LinkedList<JavaInfo>(m_components);
        // remove all components that: have parent, or can not be root
        for (Iterator<JavaInfo> I = rootComponents.iterator(); I.hasNext();) {
            JavaInfo javaInfo = I.next();
            // remove component with parent
            if (javaInfo.getParent() != null) {
                I.remove();
                continue;
            }
            // remove component that can not be root
            if (NonVisualBeanInfo.isNVO(javaInfo)) {
                I.remove();
                continue;
            }
            if (!javaInfo.canBeRoot()) {
                I.remove();
                continue;
            }
        }
        // handle "parser.preferredRoot"
        getRootComponents_preferredRoot(rootComponents);
        // in general case there are more than one root, select biggest hierarchy
        Collections.sort(rootComponents, new Comparator<JavaInfo>() {
            public int compare(JavaInfo o1, JavaInfo o2) {
                return getComponentsTreeSize(o2) - getComponentsTreeSize(o1);
            }
        });
        // OK, return root components
        return rootComponents;
    }

    /**
     * If one of the root {@link JavaInfo}'s marked as "parser.preferredRoot", keep only these roots.
     */
    private static void getRootComponents_preferredRoot(List<JavaInfo> rootComponents) {
        // check if there is JavaInfo marked with "parser.preferredRoot"
        boolean hasPreferredRoot = false;
        for (JavaInfo root : rootComponents) {
            hasPreferredRoot |= isPreferredRoot(root);
        }
        // remove JavaInfo without "parser.preferredRoot"
        if (hasPreferredRoot) {
            for (Iterator<JavaInfo> I = rootComponents.iterator(); I.hasNext();) {
                JavaInfo root = I.next();
                if (!isPreferredRoot(root)) {
                    I.remove();
                }
            }
        }
    }

    /**
     * @return <code>true</code> if given {@link JavaInfo} is marked as "parser.preferredRoot".
     */
    private static boolean isPreferredRoot(JavaInfo root) {
        // check EOL comment
        {
            int position = root.getCreationSupport().getNode().getStartPosition();
            String endOfLineComment = root.getEditor().getEndOfLineComment(position);
            if (StringUtils.contains(endOfLineComment, "@wbp.parser.preferredRoot")) {
                return true;
            }
        }
        // check description
        return JavaInfoUtils.hasTrueParameter(root, "parser.preferredRoot");
    }

    /**
     * Visitor for parsing {@link ASTNode}'s on execution flow.
     *
     * @author scheglov_ke
     */
    private final class ExecutionFlowParseVisitor extends ExecutionFlowFrameVisitor {
        private final Set<MethodDeclaration> m_visitedLazyMethods = Sets.newHashSet();

        ////////////////////////////////////////////////////////////////////////////
        //
        // Frames
        //
        ////////////////////////////////////////////////////////////////////////////
        @Override
        public boolean enterFrame(final ASTNode node) {
            // send broadcast
            ExecutionUtils.runRethrow(new RunnableEx() {
                public void run() throws Exception {
                    m_editorState.getBroadcast().getListener(ExecutionFlowEnterFrame.class).invoke(node);
                }
            });
            // enter in "lazy creation" methods, but only one time
            if (node instanceof MethodDeclaration) {
                MethodDeclaration method = (MethodDeclaration) node;
                if (m_visitedLazyMethods.contains(method)) {
                    return false;
                }
                if (LazyVariableSupportUtils.getInformation(method) != null) {
                    m_visitedLazyMethods.add(method);
                    return true;
                }
            }
            // skip factories
            if (node instanceof MethodDeclaration) {
                MethodDeclaration method = (MethodDeclaration) node;
                if (FactoryDescriptionHelper.isFactoryMethod(method)) {
                    return false;
                }
            }
            // continue
            return super.enterFrame(node);
        }

        @Override
        public void leaveFrame(final ASTNode node) {
            super.leaveFrame(node);
            // send broadcast
            ExecutionUtils.runRethrow(new RunnableEx() {
                public void run() throws Exception {
                    m_editorState.getBroadcast().getListener(EvaluationEventListener.class).leaveFrame(node);
                }
            });
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // Evaluation
        //
        ////////////////////////////////////////////////////////////////////////////
        @Override
        public void postVisit(final ASTNode node) {
            ExecutionUtils.runRethrow(new RunnableEx() {
                public void run() throws Exception {
                    // try to create JavaInfo using generic Expression
                    if (node instanceof Expression && !(node instanceof ClassInstanceCreation)
                            && !(node instanceof MethodInvocation)
                    /*&& !(node instanceof ArrayCreation)*/) {
                        endVisit((Expression) node);
                    }
                    // process related JavaInfo's
                    evaluateNode(node);
                }
            });
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // JavaInfo's creation
        //
        ////////////////////////////////////////////////////////////////////////////
        /**
         * Attempts to create {@link JavaInfo} based on some abstract {@link Expression}.
         */
        private void endVisit(Expression expression) {
            try {
                if (createJavaInfo_noModel(expression)) {
                    return;
                }
                // ask each factory, may be this creation is JavaInfo creation
                for (IParseFactory parseFactory : m_parseFactories) {
                    JavaInfo javaInfo = parseFactory.create(m_editor, expression);
                    if (javaInfo != null) {
                        addJavaInfo(javaInfo, expression);
                        break;
                    }
                }
            } catch (Throwable e) {
                ReflectionUtils.propagate(e);
            }
        }

        /**
         * @return <code>true</code> if it is known that this {@link ASTNode} must not have
         *         {@link JavaInfo} model.
         */
        private boolean createJavaInfo_noModel(ASTNode node) {
            List<ParseFactoryNoModel> validators = ExternalFactoriesHelper.getElementsInstances(
                    ParseFactoryNoModel.class, "org.eclipse.wb.core.java.parseFactories", "noModel");
            for (ParseFactoryNoModel validator : validators) {
                if (validator.noModel(node)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public void endVisit(ClassInstanceCreation creation) {
            try {
                if (createJavaInfo_noModel(creation)) {
                    return;
                }
                // prepare binding
                IMethodBinding methodBinding = AstNodeUtils.getCreationBinding(creation);
                ITypeBinding typeBinding = AstNodeUtils.getTypeBinding(creation);
                if (typeBinding == null) {
                    return;
                }
                // may be we know, that there is not model
                boolean canBeOptimized = !maybeNVO(creation);
                if (canBeOptimized && createJavaInfo_noModel(creation, typeBinding)) {
                    return;
                }
                // prepare JavaInfo arguments
                Expression arguments[] = getExpressionArray(DomGenerics.arguments(creation));
                JavaInfo[] argumentInfos = getJavaInfoArray(arguments);
                JavaInfo javaInfo = null;
                // ask each "simple" factory
                if (canBeOptimized) {
                    List<IParseFactorySimpleModelCic> validators = ExternalFactoriesHelper.getElementsInstances(
                            IParseFactorySimpleModelCic.class, "org.eclipse.wb.core.java.parseFactories",
                            "simpleModel_CIC");
                    for (IParseFactorySimpleModelCic factory : validators) {
                        if (factory.accept(m_editor, creation, typeBinding)) {
                            javaInfo = factory.create(m_editor, creation, typeBinding);
                            if (javaInfo == null) {
                                return;
                            }
                            break;
                        }
                    }
                }
                // ask each "complex" factory
                if (javaInfo == null) {
                    for (IParseFactory parseFactory : m_parseFactories) {
                        javaInfo = parseFactory.create(m_editor, creation, methodBinding, typeBinding, arguments,
                                argumentInfos);
                        if (javaInfo != null) {
                            break;
                        }
                    }
                }
                // process JavaInfo
                if (javaInfo != null) {
                    addJavaInfo(javaInfo, creation);
                    // establish parent/child link
                    bindChild_ClassInstanceCreation(creation, methodBinding, javaInfo, argumentInfos);
                    bindChild_SuperConstructorInvocation(creation, javaInfo);
                }
            } catch (Throwable e) {
                ReflectionUtils.propagate(e);
            }
        }

        /**
         * @return <code>true</code> if it is known that this {@link ClassInstanceCreation} does not
         *         have {@link JavaInfo} model.
         */
        private boolean createJavaInfo_noModel(ClassInstanceCreation creation, ITypeBinding typeBinding) {
            List<ParseFactoryNoModel> validators = ExternalFactoriesHelper.getElementsInstances(
                    ParseFactoryNoModel.class, "org.eclipse.wb.core.java.parseFactories", "noModel");
            for (ParseFactoryNoModel validator : validators) {
                if (validator.noModel(creation, typeBinding)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public void endVisit(MethodInvocation invocation) {
            try {
                if (createJavaInfo_noModel(invocation)) {
                    return;
                }
                // prepare method information
                IMethodBinding methodBinding = AstNodeUtils.getMethodBinding(invocation);
                if (methodBinding == null) {
                    return;
                }
                Expression arguments[] = getExpressionArray(DomGenerics.arguments(invocation));
                // prepare JavaInfo's
                JavaInfo expressionInfo = getJavaInfo(invocation.getExpression());
                JavaInfo[] argumentInfos = getJavaInfoArray(arguments);
                // ask each factory, may be this invocation is JavaInfo creation
                for (IParseFactory parseFactory : m_parseFactories) {
                    JavaInfo javaInfo = parseFactory.create(m_editor, invocation, methodBinding, arguments,
                            expressionInfo, argumentInfos, JavaInfoParser.this);
                    if (javaInfo != null) {
                        addJavaInfo(javaInfo, invocation);
                        break;
                    }
                }
                // establish parent/child link
                bindChild_MethodInvocation(invocation, methodBinding, expressionInfo, arguments, argumentInfos);
                // support for chain of invocations
                if (expressionInfo != null) {
                    MethodDescription methodDescription = expressionInfo.getDescription().getMethod(methodBinding);
                    if (methodDescription != null) {
                        if (methodDescription.hasTrueTag("returnThis")) {
                            expressionInfo.bindToExpression(invocation);
                        }
                    }
                }
            } catch (Throwable e) {
                ReflectionUtils.propagate(e);
            }
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // Association
        //
        ////////////////////////////////////////////////////////////////////////////
        /**
         * Sets the {@link Association} between parent/child {@link JavaInfo}'s. If <code>child</code>
         * already has {@link Association}, then {@link CompoundAssociation} will be installed.
         */
        private void bindChild_setAssociation(JavaInfo parent, JavaInfo child, Association newAssociation)
                throws Exception {
            Association existingAssociation = child.getAssociation();
            if (existingAssociation == null) {
                parent.addChild(child);
                child.setAssociation(newAssociation);
            } else {
                if (child.getParent() != parent) {
                    throwDoubleAssociationException(parent, child, existingAssociation, newAssociation);
                }
                child.setAssociation(new CompoundAssociation(existingAssociation, newAssociation));
            }
        }

        /**
         * Establish parent/child {@link Association} using given {@link MethodInvocation}.
         */
        private void bindChild_MethodInvocation(MethodInvocation invocation, IMethodBinding methodBinding,
                JavaInfo expressionInfo, Expression[] arguments, JavaInfo argumentInfos[]) throws Exception {
            if (expressionInfo != null) {
                MethodDescription methodDescription = expressionInfo.getDescription().getMethod(methodBinding);
                if (methodDescription != null) {
                    ITypeBinding[] parameterTypes = methodBinding.getParameterTypes();
                    List<ParameterDescription> parameterDescriptions = methodDescription.getParameters();
                    JavaInfo parent2Info = null;
                    JavaInfo child2Info = null;
                    // add association
                    for (ParameterDescription parameterDescription : parameterDescriptions) {
                        int parameterIndex = parameterDescription.getIndex();
                        if (parameterDescription.isChild()) {
                            JavaInfo parameterJavaInfo = argumentInfos[parameterIndex];
                            if (parameterTypes[parameterIndex].isArray()) {
                                // array parameter
                                Expression parameterExpression = arguments[parameterDescription.getIndex()];
                                if (parameterJavaInfo == null && parameterExpression instanceof ArrayCreation) {
                                    // allow only inline array creation for simple type
                                    ArrayCreation creation = (ArrayCreation) parameterExpression;
                                    if (creation.getType().getComponentType().isSimpleType()) {
                                        // prepare ArrayObjectInfo
                                        bindChild_MethodInvocationParameter_ArrayCreation(invocation,
                                                methodDescription, expressionInfo, parameterDescription, creation);
                                    }
                                } else if (parameterIndex == parameterDescriptions.size() - 1) {
                                    // ellipsis support
                                    Expression[] arrayArguments = (Expression[]) ArrayUtils.subarray(arguments,
                                            parameterIndex, arguments.length);
                                    JavaInfo[] arrayArgumentInfos = (JavaInfo[]) ArrayUtils.subarray(argumentInfos,
                                            parameterIndex, argumentInfos.length);
                                    // prepare EllipsisObjectInfo
                                    bindChild_MethodInvocationParameter_ArrayEllipsis(invocation, methodDescription,
                                            expressionInfo, parameterIndex, parameterDescription, arrayArguments,
                                            arrayArgumentInfos);
                                }
                            } else if (parameterJavaInfo != null) {
                                // simple parameter
                                bindChild_MethodInvocationParameter(invocation, methodDescription, expressionInfo,
                                        parameterDescription, parameterJavaInfo);
                            }
                        }
                        // find "secondary" association
                        if (parameterDescription.isParent2()) {
                            parent2Info = argumentInfos[parameterIndex];
                        }
                        if (parameterDescription.isChild2()) {
                            child2Info = argumentInfos[parameterIndex];
                        }
                    }
                    // set "secondary" association
                    if (parent2Info != null && child2Info != null) {
                        bindChild_setAssociation(parent2Info, child2Info,
                                new InvocationSecondaryAssociation(invocation));
                    }
                }
            }
        }

        private void bindChild_MethodInvocationParameter(MethodInvocation invocation,
                MethodDescription methodDescription, JavaInfo expressionInfo,
                ParameterDescription parameterDescription, JavaInfo parameterJavaInfo) throws Exception {
            if (parameterDescription.hasTrueTag("associateOnlyFirstTime")
                    && parameterJavaInfo.getParent() != null) {
                return;
            }
            expressionInfo.getBroadcast(JavaInfoMethodAssociationOnParse.class).invoke(expressionInfo,
                    parameterJavaInfo, methodDescription);
            bindChild_setAssociation(expressionInfo, parameterJavaInfo, new InvocationChildAssociation(invocation));
        }

        @SuppressWarnings("unchecked")
        private void bindChild_MethodInvocationParameter_ArrayCreation(MethodInvocation invocation,
                MethodDescription methodDescription, JavaInfo expressionInfo,
                ParameterDescription parameterDescription, ArrayCreation creation) throws Exception {
            ArrayObjectInfo arrayInfo = new ArrayObjectInfo(m_editor, methodDescription.getName(), creation);
            expressionInfo.addChild(arrayInfo);
            arrayInfo
                    .setRemoveOnEmpty(parameterDescription.hasTrueTag(AbstractArrayObjectInfo.REMOVE_ON_EMPTY_TAG));
            arrayInfo.setHideInTree(parameterDescription.hasTrueTag(AbstractArrayObjectInfo.HIDE_IN_TREE_TAG));
            //arrayInfo.setCreationId(parameter.getTag(ArrayObjectInfo.CREATION_ID_TAG));
            // prepare items
            Expression[] arrayItems = getExpressionArray(creation.getInitializer().expressions());
            JavaInfo[] arrayJavaInfos = getJavaInfoArray(arrayItems);
            // process array items
            for (int i = 0; i < arrayJavaInfos.length; i++) {
                JavaInfo arrayJavaInfo = arrayJavaInfos[i];
                if (arrayJavaInfo != null) {
                    if (parameterDescription.hasTrueTag("associateOnlyFirstTime")
                            && arrayJavaInfo.getParent() != null) {
                        continue;
                    }
                    Assert.isNull(arrayJavaInfo.getParent(),
                            String.format("Parent for array item %s already assigned.", arrayJavaInfo.toString()));
                    arrayInfo.addItem(arrayJavaInfo);
                    expressionInfo.getBroadcast(JavaInfoMethodAssociationOnParse.class).invoke(expressionInfo,
                            arrayJavaInfo, methodDescription);
                    bindChild_setAssociation(expressionInfo, arrayJavaInfo,
                            new InvocationChildArrayAssociation(invocation, arrayInfo));
                }
            }
        }

        @SuppressWarnings("cast")
        private void bindChild_MethodInvocationParameter_ArrayEllipsis(MethodInvocation invocation,
                MethodDescription methodDescription, JavaInfo expressionInfo, int parameterIndex,
                ParameterDescription parameterDescription, Expression[] arrayArguments, JavaInfo arrayJavaInfos[])
                throws Exception {
            Class<?> itemType = parameterDescription.getType();
            Assert.isTrue(itemType.isArray(), "Ellipsis type not array.");
            EllipsisObjectInfo arrayInfo = new EllipsisObjectInfo(m_editor, methodDescription.getName(),
                    itemType.getComponentType(), invocation, parameterIndex);
            expressionInfo.addChild(arrayInfo);
            arrayInfo
                    .setRemoveOnEmpty(parameterDescription.hasTrueTag(AbstractArrayObjectInfo.REMOVE_ON_EMPTY_TAG));
            arrayInfo.setHideInTree(parameterDescription.hasTrueTag(AbstractArrayObjectInfo.HIDE_IN_TREE_TAG));
            arrayInfo.setOnEmptySource(parameterDescription.getTag(EllipsisObjectInfo.ON_EMPTY_SOURCE_TAG));
            // process array items
            for (int i = 0; i < arrayJavaInfos.length; i++) {
                JavaInfo arrayJavaInfo = arrayJavaInfos[i];
                if (arrayJavaInfo != null) {
                    if (parameterDescription.hasTrueTag("associateOnlyFirstTime")
                            && arrayJavaInfo.getParent() != null) {
                        continue;
                    }
                    Assert.isNull(arrayJavaInfo.getParent(),
                            String.format("Parent for array item %s already assigned.", arrayJavaInfo.toString()));
                    arrayInfo.addItem(arrayJavaInfo);
                    expressionInfo.getBroadcast(JavaInfoMethodAssociationOnParse.class).invoke(expressionInfo,
                            arrayJavaInfo, methodDescription);
                    bindChild_setAssociation(expressionInfo, arrayJavaInfo,
                            new InvocationChildEllipsisAssociation(invocation, arrayInfo));
                }
            }
        }

        /**
         * Establish parent/child {@link Association} using given {@link ClassInstanceCreation}.
         */
        private void bindChild_ClassInstanceCreation(ClassInstanceCreation creation, IMethodBinding methodBinding,
                JavaInfo javaInfo, JavaInfo argumentInfos[]) throws Exception {
            if (javaInfo.getParent() == null) {
                ConstructorDescription constructor = javaInfo.getDescription().getConstructor(methodBinding);
                if (constructor != null) {
                    for (ParameterDescription parameter : constructor.getParameters()) {
                        // parent passed as constructor argument
                        if (parameter.isParent()) {
                            JavaInfo parentInfo = argumentInfos[parameter.getIndex()];
                            if (parentInfo != null) {
                                parentInfo.addChild(javaInfo);
                                javaInfo.setAssociation(new ConstructorParentAssociation());
                            }
                            break;
                        }
                        // child passed as constructor argument
                        if (parameter.isChild()) {
                            JavaInfo childInfo = argumentInfos[parameter.getIndex()];
                            if (childInfo != null) {
                                javaInfo.addChild(childInfo);
                                childInfo.setAssociation(new ConstructorChildAssociation());
                            }
                            break;
                        }
                    }
                }
            }
        }

        /**
         * New {@link JavaInfo} was just created using given {@link Expression}. This method performs
         * special check, if this {@link Expression} is argument of {@link SuperConstructorInvocation},
         * so sets object and parent/child link.
         * <p>
         * For example: <code><pre>
         *   public class Test extends JPanel {
         *     public Test() {
         *       this(new BorderLayout());
         *     }
         *   }
         * </pre><code>
         */
        private void bindChild_SuperConstructorInvocation(Expression expression, JavaInfo javaInfo)
                throws Exception {
            if (javaInfo.getParent() == null) {
                if (expression.getLocationInParent() == SuperConstructorInvocation.ARGUMENTS_PROPERTY) {
                    SuperConstructorInvocation constructorInvocation = (SuperConstructorInvocation) expression
                            .getParent();
                    IMethodBinding constructorBinding = AstNodeUtils.getSuperBinding(constructorInvocation);
                    // set Object
                    {
                        Object object = JavaInfoEvaluationHelper.getValue(expression);
                        javaInfo.setObject(object);
                    }
                    // set parent/child link
                    {
                        JavaInfo thisJavaInfo = getJavaInfo(null);
                        ConstructorDescription constructorDescription = thisJavaInfo.getDescription()
                                .getConstructor(constructorBinding);
                        if (constructorDescription != null) {
                            int argumentIndex = constructorInvocation.arguments().indexOf(expression);
                            ParameterDescription parameter = constructorDescription.getParameter(argumentIndex);
                            // child passed as SuperConstructorInvocation argument
                            if (parameter.isChild()) {
                                thisJavaInfo.addChild(javaInfo);
                                Association association = new SuperConstructorArgumentAssociation(
                                        constructorInvocation);
                                javaInfo.setAssociation(association);
                            }
                        }
                    }
                }
            }
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // Variables
        //
        ////////////////////////////////////////////////////////////////////////////
        @Override
        public void endVisit(VariableDeclarationFragment declaration) {
            if (createJavaInfo_noModel(declaration)) {
                return;
            }
            SimpleName variable = declaration.getName();
            Expression initializer = declaration.getInitializer();
            createVariableSupport(variable, initializer);
        }

        @Override
        public void endVisit(Assignment assignment) {
            if (createJavaInfo_noModel(assignment)) {
                return;
            }
            Expression variable = assignment.getLeftHandSide();
            if (AstNodeUtils.isVariable(variable)) {
                Expression initializer = assignment.getRightHandSide();
                createVariableSupport(variable, initializer);
            }
        }

        private void createVariableSupport(final Expression variable, final Expression initializer) {
            ExecutionUtils.runRethrow(new RunnableEx() {
                public void run() throws Exception {
                    JavaInfo javaInfo = getJavaInfo(initializer);
                    if (javaInfo != null) {
                        // check if there is already variable support
                        boolean noVariableSupport;
                        {
                            VariableSupport support = javaInfo.getVariableSupport();
                            noVariableSupport = support == null || support.isDefault();
                        }
                        // only first assignment is variable
                        if (noVariableSupport) {
                            VariableSupport support;
                            // prepare assignments and binding
                            List<Expression> assignments = ExecutionFlowUtils
                                    .getAssignments(m_editorState.getFlowDescription(), variable);
                            IVariableBinding variableBinding = AstNodeUtils.getVariableBinding(variable);
                            // local or field
                            if (variableBinding.isField()) {
                                if (assignments.size() == 1) {
                                    if (variable.getParent() instanceof VariableDeclarationFragment) {
                                        support = new FieldInitializerVariableSupport(javaInfo, variable);
                                    } else {
                                        support = new FieldUniqueVariableSupport(javaInfo, variable);
                                    }
                                } else {
                                    support = new FieldReuseVariableSupport(javaInfo, variable);
                                }
                            } else {
                                SimpleName simpleVariable = (SimpleName) variable;
                                if (assignments.size() == 1) {
                                    support = new LocalUniqueVariableSupport(javaInfo, simpleVariable);
                                } else {
                                    support = new LocalReuseVariableSupport(javaInfo, simpleVariable);
                                }
                            }
                            // set variable support
                            javaInfo.setVariableSupport(support);
                        }
                    }
                }
            });
        }
    }

    /**
     * @return the {@link ParseRootContext} for beginning parsing
     */
    private ParseRootContext prepareParseContext() throws Exception {
        // prepare type and binding
        IType primaryType;
        ITypeBinding typeBinding;
        {
            ICompilationUnit modelUnit = m_editor.getModelUnit();
            String unitPath = modelUnit.getUnderlyingResource().getFullPath().toPortableString();
            primaryType = CodeUtils.findPrimaryType(modelUnit);
            Assert.isTrueException(primaryType != null, ICoreExceptionConstants.PARSER_NO_PRIMARY_TYPE, unitPath);
            String primaryTypeName = primaryType.getElementName();
            m_typeDeclaration = AstNodeUtils.getTypeByName(m_editor.getAstUnit(), primaryTypeName);
            Assert.isTrueException(m_typeDeclaration != null, ICoreExceptionConstants.PARSER_NO_TYPE_DECLARATION,
                    unitPath);
            typeBinding = m_typeDeclaration.resolveBinding();
            Assert.isNotNull(typeBinding);
        }
        // ask each factory for root methods
        for (IParseFactory parseFactory : m_parseFactories) {
            ParseRootContext rootContext = parseFactory.getRootContext(m_editor, m_typeDeclaration, typeBinding);
            if (rootContext != null) {
                return rootContext;
            }
        }
        // check that one of the parse factories identified toolkit
        {
            EditorState editorState = EditorState.get(m_editor);
            if (editorState.getEditorLoader() == null) {
                if (!canBeClassWithGUI()) {
                    throw new DesignerException(ICoreExceptionConstants.PARSER_NOT_GUI,
                            primaryType.getFullyQualifiedName());
                }
                throw new DesignerException(ICoreExceptionConstants.PARSER_NO_TOOLKIT,
                        primaryType.getFullyQualifiedName());
            }
        }
        // no root context found, try to find "main"
        {
            MethodDeclaration mainMethod = AstNodeUtils.getMethodBySignature(m_typeDeclaration,
                    "main(java.lang.String[])");
            if (mainMethod != null) {
                return new ParseRootContext(null, new ExecutionFlowDescription(mainMethod));
            }
        }
        // still no entry point, try to use constructor
        {
            MethodDeclaration constructor = ExecutionFlowUtils.getExecutionFlowConstructor(m_typeDeclaration);
            if (constructor != null) {
                return new ParseRootContext(null, new ExecutionFlowDescription(constructor));
            }
        }
        // special handling for factories
        AbstractParseFactory.failIfFactory(m_editor, m_typeDeclaration, typeBinding);
        // can not find root methods, fail
        m_editorState.getBroadcast().getListener(ObjectEventListener.class).dispose();
        throw new NoEntryPointError(m_editor, m_typeDeclaration);
    }

    /**
     * Checks if {@link CompilationUnit} can have GUI, but we were not able no understand it because
     * of no corresponding GUI toolkit. Right now we just check that there are
     * {@link ClassInstanceCreation}'s, so for example identify empty classes or data beans.
     */
    private boolean canBeClassWithGUI() {
        final AtomicBoolean result = new AtomicBoolean();
        m_editor.getAstUnit().accept(new ASTVisitor() {
            @Override
            public void endVisit(ClassInstanceCreation node) {
                result.set(true);
            }
        });
        return result.get();
    }

    /**
     * Adds new given {@link JavaInfo} to the list of components.
     */
    private void addJavaInfo(JavaInfo javaInfo, Expression creation) throws Exception {
        // add component
        m_javaInfoResolver.addJavaInfo(javaInfo, creation);
        javaInfo.addRelatedNode(creation);
        // activate Java state
        GlobalStateJava.activate(javaInfo);
        // set default variable support
        {
            // check for "lazy creation" pattern
            if (javaInfo.getVariableSupport() == null) {
                LazyVariableSupportUtils.setLazyVariable(javaInfo);
            }
            // set "empty" variable support
            if (javaInfo.getVariableSupport() == null) {
                Assert.isNotNull(creation);
                javaInfo.setVariableSupport(new EmptyVariableSupport(javaInfo, creation));
            }
        }
        // evaluate "this"
        if (!javaInfo.getCreationSupport().canBeEvaluated()) {
            getEvaluationHelper().evaluateJavaInfoUsingCreationSupport(javaInfo);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Evaluation
    //
    ////////////////////////////////////////////////////////////////////////////
    private final Set<ASTNode> m_evaluatedNodes = Sets.newHashSet();
    private JavaInfoEvaluationHelper m_evaluationHelper;

    /**
     * @return the {@link JavaInfoEvaluationHelper} for this {@link AstEditor}.
     */
    private JavaInfoEvaluationHelper getEvaluationHelper() {
        if (m_evaluationHelper == null) {
            m_evaluationHelper = new JavaInfoEvaluationHelper(m_editor, getParseVisitor()) {
                @Override
                protected JavaInfo getRootJavaInfo() {
                    return m_rootComponent;
                }

                @Override
                protected JavaInfo getJavaInfoRepresentedBy(Expression expression) {
                    return getJavaInfo(expression);
                }

                @Override
                protected void thisJavaInfoNodeProcessed(JavaInfo javaInfo, ASTNode node) throws Exception {
                    javaInfo.addRelatedNode(node);
                }
            };
        }
        return m_evaluationHelper;
    }

    /**
     * Evaluates given {@link ASTNode} using {@link JavaInfoEvaluationHelper}.
     */
    private void evaluateNode(ASTNode node) throws Exception {
        if (!m_evaluatedNodes.contains(node)) {
            m_evaluatedNodes.add(node);
            try {
                JavaInfoEvaluationHelper evaluationHelper = getEvaluationHelper();
                // send notifications and evaluate
                if (shouldNotifyAboutEvaluate(node)) {
                    m_evaluationListener.evaluateBefore(null, node);
                }
                evaluationHelper.evaluate(node);
                if (shouldNotifyAboutEvaluate(node)) {
                    m_evaluationListener.evaluateAfter(null, node);
                }
            } catch (Throwable e) {
                // fail, if fatal
                if (DesignerExceptionUtils.isFatal(e)) {
                    ReflectionUtils.propagate(e);
                }
                // exception during JavaInfo evaluation is serious, so fail
                if (node instanceof Expression && getJavaInfo((Expression) node) != null) {
                    ReflectionUtils.propagate(e);
                }
                // some other exception, log and continue
                m_editorState.getBadParserNodes().add(node, e);
            }
        }
    }

    private static boolean shouldNotifyAboutEvaluate(ASTNode node) {
        return node instanceof ClassInstanceCreation || node instanceof MethodInvocation
                || node instanceof Statement;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Utils
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the array of {@link Expression}'s for given list of them.
     */
    private static Expression[] getExpressionArray(List<Expression> expressionList) {
        return expressionList.toArray(new Expression[expressionList.size()]);
    }

    /**
     * @return the size (count of elements) in components tree starting from given root.
     */
    private static int getComponentsTreeSize(ObjectInfo info) {
        int size = 1;
        // add sizes of children
        for (ObjectInfo child : info.getChildren()) {
            size += getComponentsTreeSize(child);
        }
        // return final size
        return size;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // JavaInfo access
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the {@link JavaInfo} for given {@link Expression} or <code>null</code> if given
     *         {@link Expression} does not represent {@link JavaInfo}.
     */
    public JavaInfo getJavaInfo(Expression expression) {
        return m_javaInfoResolver.getJavaInfo(expression);
    }

    /**
     * @return the array of {@link JavaInfo}'s represented by given array of {@link Expression}'s.
     */
    private JavaInfo[] getJavaInfoArray(Expression expressions[]) throws Exception {
        JavaInfo javaInfos[] = new JavaInfo[expressions.length];
        for (int i = 0; i < expressions.length; i++) {
            Expression expression = expressions[i];
            javaInfos[i] = getJavaInfo(expression);
        }
        return javaInfos;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Exceptions
    //
    ////////////////////////////////////////////////////////////////////////////
    private void throwDoubleAssociationException(JavaInfo parent, JavaInfo child, Association existingAssociation,
            Association newAssociation) throws DesignerException {
        String existingStatementSource = getAssociationStatementSource(existingAssociation);
        String newStatementSource = getAssociationStatementSource(newAssociation);
        String message = MessageFormat.format(
                "\nChild:                    \n  {0}" + "\nExisting: \n  {1} \n    {2}"
                        + "\nNew: \n  {3} \n    {4}",
                child, child.getParent(), existingAssociation, parent, newAssociation);
        DesignerException designerWarning = new DesignerException(ICoreExceptionConstants.PARSER_DOUBLE_ASSOCIATION,
                getShortComponentName(child), existingStatementSource, newStatementSource, message);
        designerWarning.setSourcePosition(m_visitor.m_currentStatement.getStartPosition());
        throw designerWarning;
    }

    /**
     * @return the source of {@link Association} statement or <code>"null"</code>.
     */
    private String getAssociationStatementSource(Association association) {
        Statement statement = association.getStatement();
        return statement != null ? m_editor.getSource(statement) : "<null>";
    }

    private static String getShortComponentName(JavaInfo javaInfo) {
        if (javaInfo == null) {
            return "<null>";
        }
        return javaInfo.getVariableSupport().getComponentName();
    }

    /**
     * @return <code>true</code> if given {@link ClassInstanceCreation} may be NVO creation, so sohuld
     *         be handled without optimizations.
     */
    private static boolean maybeNVO(ClassInstanceCreation creation) {
        BodyDeclaration body = AstNodeUtils.getEnclosingNode(creation, BodyDeclaration.class);
        if (body != null) {
            Javadoc doc = body.getJavadoc();
            return doc != null && doc.toString().contains("@wbp.nonvisual");
        }
        return false;
    }
}