flex2.compiler.mxml.rep.MxmlDocument.java Source code

Java tutorial

Introduction

Here is the source code for flex2.compiler.mxml.rep.MxmlDocument.java

Source

/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package flex2.compiler.mxml.rep;

import flash.util.StringUtils;
import flex2.compiler.CompilationUnit;
import flex2.compiler.CompilerContext;
import flex2.compiler.css.Styles;
import flex2.compiler.css.StylesContainer;
import flex2.compiler.mxml.MxmlConfiguration;
import flex2.compiler.mxml.dom.DesignLayerNode;
import flex2.compiler.mxml.dom.Node;
import flex2.compiler.mxml.gen.CodeFragmentList;
import flex2.compiler.mxml.gen.DescriptorGenerator;
import flex2.compiler.mxml.gen.StatesGenerator;
import flex2.compiler.mxml.gen.TextGen;
import flex2.compiler.mxml.lang.FrameworkDefs;
import flex2.compiler.mxml.lang.StandardDefs;
import flex2.compiler.mxml.reflect.Property;
import flex2.compiler.mxml.reflect.Type;
import flex2.compiler.mxml.reflect.TypeTable;
import flex2.compiler.mxml.rep.decl.InitializedPropertyDeclaration;
import flex2.compiler.mxml.rep.decl.PropertyDeclaration;
import flex2.compiler.mxml.rep.decl.UninitializedPropertyDeclaration;
import flex2.compiler.mxml.rep.init.Initializer;
import flex2.compiler.mxml.rep.init.NamedInitializer;
import flex2.compiler.mxml.rep.init.ValueInitializer;
import flex2.compiler.mxml.rep.init.EventInitializer;
import flex2.compiler.mxml.rep.StatesModel;
import flex2.compiler.util.*;

import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.iterators.FilterIterator;
import org.apache.commons.collections.iterators.IteratorChain;

import java.util.*;

/**
 * This class represents an Mxml document's class information and
 * contains the object representation of the document's declarations,
 * binding expressions, @Embed's, @Resource's, and styles.  It is
 * created by the ImplemenationCompiler and filled in by
 * DocumentBuilder's analysis of the DOM.
 *
 * @see flex2.compiler.mxml.ImplementationCompiler
 * @see flex2.compiler.mxml.builder.DocumentBuilder
 */
public final class MxmlDocument {
    private final CompilationUnit unit;
    private final TypeTable typeTable;
    private final DocumentInfo info;
    private final StandardDefs standardDefs;

    private Model root;
    private final Map<String, PropertyDeclaration> declarations;
    private final List<BindingExpression> bindingExpressions;
    private final List<PropertyDeclaration> layerDeclarations;
    private final Map<String, AtEmbed> atEmbeds;
    private final Map<String, AtResource> atResources;
    private final Set<String> typeRefs;
    private final StylesContainer stylesContainer;

    private String preloader;
    private boolean usePreloader;

    private DualModeLineNumberMap lineNumberMap;

    private Map<String, Integer> anonIdCounts;

    private boolean bindingImportsAdded; //  HACK see ensureBindingImports()

    private Map sharedObjects;

    private StatesModel statesModel;
    private List statefulEventInitializers;
    private String comment;

    private boolean showDeprecationWarnings;
    private boolean allowDuplicateDefaultStyleDeclarations;

    private Map<DesignLayerNode, DesignLayer> designLayers;

    public MxmlDocument(CompilationUnit unit, TypeTable typeTable, DocumentInfo info,
            MxmlConfiguration mxmlConfiguration) {
        this.unit = unit;
        this.typeTable = typeTable;
        this.info = info;
        this.standardDefs = unit.getStandardDefs();

        root = null;
        declarations = new TreeMap<String, PropertyDeclaration>();
        layerDeclarations = new ArrayList<PropertyDeclaration>();
        bindingExpressions = new ArrayList<BindingExpression>();
        atEmbeds = new TreeMap<String, AtEmbed>();
        atResources = new TreeMap<String, AtResource>();
        typeRefs = new TreeSet<String>();

        designLayers = new HashMap<DesignLayerNode, DesignLayer>();

        stylesContainer = new StylesContainer(mxmlConfiguration, unit, typeTable.getPerCompileData());
        unit.setStylesContainer(stylesContainer);
        showDeprecationWarnings = mxmlConfiguration.showDeprecationWarnings();
        allowDuplicateDefaultStyleDeclarations = mxmlConfiguration.getAllowDuplicateDefaultStyleDeclarations();

        stylesContainer.setMxmlDocument(this);
        stylesContainer.setNameMappings(typeTable.getNameMappings());
        sharedObjects = new TreeMap();

        statesModel = new StatesModel(this, info, standardDefs);
        statefulEventInitializers = new ArrayList();

        preloader = NameFormatter.toDot(standardDefs.CLASS_DOWNLOADPROGRESSBAR);
        usePreloader = true;

        lineNumberMap = null;

        anonIdCounts = new HashMap<String, Integer>();

        bindingImportsAdded = false;

        //  transfer binding expressions out to CompilerContext
        //  TODO this should happen somewhere else
        CompilerContext context = unit.getContext();
        context.setAttribute(CompilerContext.BINDING_EXPRESSIONS, bindingExpressions);
    }

    public final DesignLayer getLayerModel(DesignLayerNode node) {
        return designLayers.get(node);
    }

    public final void addLayerModel(DesignLayerNode node, DesignLayer model) {
        designLayers.put(node, model);
    }

    public final CompilationUnit getCompilationUnit() {
        return unit;
    }

    public final String getSourcePath() {
        return unit.getSource().getName();
    }

    public final StandardDefs getStandardDefs() {
        return standardDefs;
    }

    public final boolean getIsMain() {
        return unit.isRoot();
    }

    public final TypeTable getTypeTable() {
        return typeTable;
    }

    public final String getClassName() {
        return info.getClassName();
    }

    public final String getConvertedClassName() {
        return "_" + StringUtils.substitute(getClassName(), ".", "_");
    }

    public final String getPackageName() {
        return info.getPackageName();
    }

    public final QName getQName() {
        return info.getQName();
    }

    public final Type getSkeletonClass() {
        return typeTable.getType(getQName().toString());
    }

    public final Type getSuperClass() {
        return getRoot().getType();
    }

    public final String getSuperClassName() {
        return NameFormatter.toDot(getSuperClass().getName());
    }

    public final boolean getHasInterfaces() {
        return info.getInterfaceNames().size() > 0;
    }

    public final boolean getIsInlineComponent() {
        return info.getRootNode().isInlineComponent();
    }

    public final boolean getAllowDuplicateDefaultStyleDeclarations() {
        return allowDuplicateDefaultStyleDeclarations;
    }

    /*
     * TODO set this and various other stuff from Info, at construction time
     */
    public void setRoot(Model root) {
        this.root = root;

        //  TODO what follows can move into ctor once root is set there from info

        if (getIsContainer()) {
            addImport(NameFormatter.toDot(standardDefs.CLASS_UICOMPONENTDESCRIPTOR), root.getXmlLineNumber());
        }

        String outerDocClassName = info.getRootNode().getOuterDocumentClassName();
        if (outerDocClassName != null) {
            addDeclaration(DocumentInfo.OUTER_DOCUMENT_PROP, outerDocClassName, 0, false, true, false, false);
        }
    }

    /**
     *
     */
    public final Model getRoot() {
        assert root != null : "root component not set";
        return root;
    }

    /**
     *
     */
    public final void addDeclaration(Model model, boolean topLevel) {
        if (!inheritedPropertyUsageError(model.getId(), model.getType(), model.getXmlLineNumber())) {
            if (model instanceof DesignLayer)
                layerDeclarations
                        .add(new InitializedPropertyDeclaration(model, topLevel, model.getXmlLineNumber()));

            declarations.put(model.getId(),
                    new InitializedPropertyDeclaration(model, topLevel, model.getXmlLineNumber()));
        }
    }

    /**
     *
     */
    public final void addDeclaration(String id, String typeName, int line, boolean inspectable, boolean topLevel,
            boolean idIsAutogenerated, boolean isBindable) {
        addDeclaration(id, typeName, line, inspectable, topLevel, idIsAutogenerated, isBindable, null);
    }

    public final void addDeclaration(String id, String typeName, int line, boolean inspectable, boolean topLevel,
            boolean idIsAutogenerated, boolean isBindable, String comment) {
        if (!inheritedPropertyUsageError(id, root.getType().getTypeTable().getType(NameFormatter.toColon(typeName)),
                line)) {
            declarations.put(id, new UninitializedPropertyDeclaration(id, typeName, line, inspectable, topLevel,
                    idIsAutogenerated, isBindable, comment));
        }
    }

    /**
     * Return a property declaration from our declarations map.
     */
    public final PropertyDeclaration getDeclaration(String id) {
        return declarations.get(id);
    }

    /**
     * Register state specific event initializer
     */
    public final void addStateSpecificEventInitializer(EventInitializer initializer) {
        if (!initializer.getHandlerText().equals("@Clear()")) {
            statefulEventInitializers.add(initializer);
        }
    }

    /**
     * If a model is not by default to be declared, declare it otherwise.
     */
    public final void ensureDeclaration(Model model) {
        if (!isDeclared(model)) {
            addDeclaration(model, false);
        }
    }

    /**
     * true iff the document has a property (induced or explicit) named by the model's id
     */
    // TODO remove
    public final boolean isDeclared(Model model) {
        String id = model.getId();
        return id != null && isDeclared(id);
    }

    /**
     * true iff the document has a property (induced or explicit) named by the id
     */
    public final boolean isDeclared(String id) {
        return declarations.containsKey(id);
    }

    public boolean showDeprecationWarnings() {
        return showDeprecationWarnings;
    }

    /**
     * NOTE: suppress declaration of inherited properties
     */
    public final Iterator<PropertyDeclaration> getDeclarationIterator() {
        final Type superType = getSuperClass();

        return new FilterIterator(declarations.values().iterator(), new Predicate() {
            public boolean evaluate(Object object) {
                return superType.getProperty(((PropertyDeclaration) object).getName()) == null;
            }
        });
    }

    /**
     *
     */
    private final Iterator<PropertyDeclaration> getTopLevelDeclarationIterator() {
        return new FilterIterator(declarations.values().iterator(), new Predicate() {
            public boolean evaluate(Object object) {
                return ((PropertyDeclaration) object).getTopLevel();
            }
        });
    }

    /**
     *
     */
    public final Iterator<Initializer> getNonStagePropertyInitializerIterator() {
        return new FilterIterator(
                new IteratorChain(root.getPropertyInitializerIterator(false), getTopLevelInitializerIterator()),
                new Predicate() {
                    public boolean evaluate(Object object) {
                        if (object instanceof NamedInitializer)
                            return (!StandardDefs.isStageProperty(((NamedInitializer) object).getName()))
                                    && (!((NamedInitializer) object).isDesignLayer());
                        return true;
                    }
                });
    }

    /**
    *
    */
    public final Iterator<Initializer> getDesignLayerPropertyInitializerIterator() {
        return new FilterIterator(layerDeclarations.iterator(), new Predicate() {
            public boolean evaluate(Object object) {
                return object instanceof InitializedPropertyDeclaration;
            }
        });
    }

    /**
     *
     */
    public final Iterator<Initializer> getStagePropertyInitializerIterator() {
        return new FilterIterator(
                new IteratorChain(root.getPropertyInitializerIterator(false), getTopLevelInitializerIterator()),
                new Predicate() {
                    public boolean evaluate(Object object) {
                        if (object instanceof NamedInitializer)
                            return StandardDefs.isStageProperty(((NamedInitializer) object).getName());
                        return false;
                    }
                });
    }

    /**
     * 
     */
    public final boolean getHasStagePropertyInitializers() {
        return getStagePropertyInitializerIterator().hasNext();
    }

    /**
      *
      */
    public final Iterator<Initializer> getTopLevelInitializerIterator() {
        return new FilterIterator(getTopLevelDeclarationIterator(), new Predicate() {
            public boolean evaluate(Object object) {
                return object instanceof InitializedPropertyDeclaration;
            }
        });
    }

    /**
     * a little trickiness here: we need to initialize both our superclass properties, and document variables that
     * have initializers
     */
    public final Iterator<Initializer> getPropertyInitializerIterator() {
        return new IteratorChain(root.getPropertyInitializerIterator(false), getTopLevelInitializerIterator());
    }

    /*
     * State-specific event handler iterator.
     */
    public final Iterator<Initializer> getStatefulEventIterator() {
        return statefulEventInitializers.iterator();
    }

    /**
     * return an iterator over visual children that haven't been marked described.
     */
    // TODO visual children are marked described by the descriptor
    //      generator, so there is some order-of-codegen sensitivity
    //      here. It's the only such dependency, but at some point
    //      descriptor codegen and marking-of-isDescribed should be
    //      split apart.
    public final Iterator getProceduralVisualChildInitializerIterator() {
        if (root instanceof MovieClip) {
            return new FilterIterator(((MovieClip) root).getChildInitializerIterator(), new Predicate() {
                public boolean evaluate(Object object) {
                    ValueInitializer init = (ValueInitializer) object;
                    Object value = init.getValue();
                    return !(value instanceof Model)
                            || (!((Model) value).isDescribed() && !((Model) value).isStateSpecific());
                }
            });
        } else {
            return Collections.EMPTY_LIST.iterator();
        }
    }

    /**
     * For Flex 2.0, visual children are always described - i.e., initialized using the UICOmponentDescriptor-based
     * DI machinery, rather than by procedural code. Future variations of this approach can be tested by modifying
     * what this method returns - e.g. by querying a config variable, making a per-document decision (say, based on
     * metadata or base class), or whatever.
     *
     * (Note that DI is always used for Repeater contents, independent of this setting.)
     */
    public final boolean getDescribeVisualChildren() {
        return true;
    }

    /**
     * iterator over all definitions from our toplevel declarations, root initializers,
     * and our states model (state specific values).
     */
    public final Iterator<CodeFragmentList> getDefinitionIterator() {
        IteratorList iterList = new IteratorList();

        Model.addDefinitionIterators(iterList, getTopLevelInitializerIterator());

        Iterator iter = statefulEventInitializers.iterator();
        while (iter.hasNext()) {
            iterList.add(((Initializer) iter.next()).getDefinitionsIterator());
        }

        iterList.add(root.getSubDefinitionsIterator());

        if (getVersion() >= 4)
            iterList.add(statesModel.getSubDefinitionIterators());

        return iterList.toIterator();
    }

    /**
     *
     */
    public final void addBindingExpression(BindingExpression expr) {
        expr.setId(bindingExpressions.size());
        bindingExpressions.add(expr);
        info.addInterfaceName(standardDefs.INTERFACE_IBINDINGCLIENT_DOT, -1);
    }

    /**
     *
     */
    public final void removeBindingExpression(BindingExpression expr) {
        int index = bindingExpressions.indexOf(expr);
        if (index >= 0) {
            for (int i = index; i < bindingExpressions.size(); i++)
                bindingExpressions.get(i).setId(i - 1);

            bindingExpressions.remove(index);
        }
    }

    public final List<BindingExpression> getBindingExpressions() {
        return bindingExpressions;
    }

    public final void addAtEmbed(AtEmbed atEmbed) {
        if (!atEmbeds.containsKey(atEmbed.getPropName())) {
            atEmbeds.put(atEmbed.getPropName(), atEmbed);
        }
    }

    public final Set<AtEmbed> getAtEmbeds() {
        Set<AtEmbed> result = new HashSet(atEmbeds.values());

        if (stylesContainer != null) {
            result.addAll(stylesContainer.getAtEmbeds());
        }

        return result;
    }

    public final boolean addAtResource(AtResource atResource) {
        // @Resource codegen (AtResource.getValueExpression()) requires mx.resources.ResourceManager
        addImport(standardDefs.CLASS_RESOURCEMANAGER_DOT, atResource.getXmlLineNumber());
        atResources.put(atResource.getBundle(), atResource);
        return true;
    }

    public final Collection<AtResource> getAtResources() {
        return atResources.values();
    }

    /**
     *
     */
    public final void addTypeRef(String typeRef, int line) {
        addImport(typeRef, line);
        typeRefs.add(typeRef);
    }

    public final Collection<String> getTypeRefs() {
        return typeRefs;
    }

    /**
     *
     */
    public final void addImport(String name, int line) {
        info.addImportName(name, line);
    }

    public final Set<DocumentInfo.NameInfo> getImports() {
        ensureBindingImports();
        return info.getImportNames();
    }

    public final Collection<String[]> getSplitImports() {
        return info.getSplitImportNames();
    }

    //  HACK: because essential stuff in a BindingExpression is set up *after* addBindingExpression() is called
    //  on it, we have to wait until the last minute before adding their destination types to the import list.
    //  TODO clean up BindingExpression setup. A BE should be completely configured by the time it's added here.
    private final void ensureBindingImports() {
        if (!bindingImportsAdded) {
            for (Iterator<BindingExpression> iter = bindingExpressions.iterator(); iter.hasNext();) {
                BindingExpression expr = iter.next();
                addImport(expr.getDestinationTypeName(), expr.getXmlLineNumber());
            }
            bindingImportsAdded = true;
        }
    }

    /**
     *
     */
    public final void addScript(Script script) {
        info.addScript(script);
    }

    public final List<Script> getScripts() {
        return info.getScripts();
    }

    /**
     * Looks to see whether the document has a local class mapping for a
     * qualified tag name.
     *
     * @param namespace The tag namespace URI.
     * @param localPart The tag name.
     * @return The Class name, or null if a mapping was not found.
     */
    public String getLocalClass(String namespace, String localPart) {
        return info.getLocalClass(namespace, localPart);
    }

    /**
     *
     */
    public final void addMetadata(Script metaDataSource) {
        String text = metaDataSource.getText();
        assert text != null;

        // FIXME - when people stop abusing metadata, this hack can be nuked.
        if (!text.startsWith("[")) {
            info.getMetadata().add(0, metaDataSource);
        } else {
            info.addMetadata(metaDataSource);
        }
    }

    public final List<Script> getMetadata() {
        return info.getMetadata();
    }

    /**
     *
     */
    public StylesContainer getStylesContainer() {
        return stylesContainer;
    }

    /**
     *
     */
    public Iterator getInheritingStyleNameIterator() {
        final Styles styles = typeTable.getStyles();
        return new FilterIterator(styles.getStyleNames(), new Predicate() {
            public boolean evaluate(Object obj) {
                return styles.isInheritingStyle((String) obj);
            }
        });
    }

    /**
     * Returns true if root's type implements mx.core.IContainer or mx.core.Repeater.
     */
    public boolean getIsContainer() {
        return standardDefs.isContainer(root.getType());
    }

    /**
     * Returns true if root's type implements mx.core.IVisualElementContainer.
     */
    public boolean getIsVisualElementContainer() {
        return root.getType().isAssignableTo(standardDefs.INTERFACE_IVISUALELEMENTCONTAINER);
    }

    public boolean getIsIFlexModule() {
        return standardDefs.isIFlexModule(root.getType());
    }

    public boolean getIsIUIComponent() {
        return standardDefs.isIUIComponent(root.getType());
    }

    /**
     * Test if this document is a Flex application.
     * 
     * @return True if this is a Flex application, false if
     * this document is a non-Flex application.  
     */
    public boolean getIsFlexApplication() {
        return getIsMain() && (getIsContainer() || (getVersion() >= 4 && getIsSimpleStyleComponent()));
    }

    public boolean getIsSimpleStyleComponent() {
        return standardDefs.isSimpleStyleComponent(root.getType());
    }

    public final void setLineNumberMap(DualModeLineNumberMap lineNumberMap) {
        this.lineNumberMap = lineNumberMap;
    }

    public final DualModeLineNumberMap getLineNumberMap() {
        return lineNumberMap;
    }

    public final void setPreloader(String preloader) {
        this.preloader = preloader;
    }

    public final String getPreloader() {
        return preloader;
    }

    public void setUsePreloader(boolean usePreloader) {
        this.usePreloader = usePreloader;
    }

    public final boolean getUsePreloader() {
        return usePreloader;
    }

    public String getLanguageNamespace() {
        return info.getLanguageNamespace();
    }

    public int getVersion() {
        return info.getVersion();
    }

    /**
     * NOTE the phase situation here if super is MXML that is being compiled in this run. In that case, this call will
     * be examining the "public signature" type put together by InterfaceCompiler, *not* the fully built component.
     */
    public final boolean superHasPublicProperty(String name) {
        return getSuperClass().getProperty(name) != null;
    }

    /**
     *
     */
    void ensureId(Model model) {
        if (model.getId() == null) {
            Type type = model.getType();
            assert type != null;

            int i = getAnonIndex(model.getType());

            String id = "_" + NameFormatter.toDot(info.getClassName()).replace('.', '_') + "_"
                    + NameFormatter.retrieveClassName(type.getName().replace('<', '_').replace('>', '_')) + i;
            // String id = "_" + NameFormatter.retrieveClassName(type.getName()) + i;

            model.setId(id, true);
        }
    }

    /**
     * Note: we use the leaf name as our key, rather than the full classname (Foo rather than a.b.c.Foo).
     * This allows us to use generated function names like _Foo, rather than _a_b_c_Foo, for readability.
     */
    private int getAnonIndex(Type type) {
        String typeName = NameFormatter.retrieveClassName(type.getName());
        Integer cell = anonIdCounts.get(typeName);
        int i = cell == null ? 1 : cell.intValue();
        anonIdCounts.put(typeName, new Integer(i + 1));
        return i;
    }

    /**
     * Match up two-way binding expressions and setTwoWayCounterpart to indicate partner. 
     */
    public void resolveTwoWayBindings() {
        // If there are any two-way bindings, only partially built, now is the time
        // to finish them.
        completeTwoWayBindings();

        Map<String, BindingExpression> destinationMap = new HashMap<String, BindingExpression>();

        for (BindingExpression bindingExpression : bindingExpressions) {
            // Note that this just strips the parens on the edges of the expression.
            // It's possible the expression was an inline expression which got parsed to
            // something like '(a.text) + "." + (b.text)'.  Since this would never be part of a two-way bind
            // it probably doesn't matter that when the parens are stripped it ends up
            // as 'a.text) + "." + (b.text'.
            String sourceExpression = TextGen.stripParens(bindingExpression.getSourceExpression());

            String destinationPath = bindingExpression.getDestinationPath(false);
            BindingExpression match = destinationMap.get(sourceExpression);

            if ((match != null) && destinationPath.equals(TextGen.stripParens(match.getSourceExpression()))) {
                bindingExpression.setTwoWayCounterpart(match);
            } else {
                destinationMap.put(destinationPath, bindingExpression);
            }
        }
    }

    /**
     * If a binding expression has isTwoWayPrimary set, this signals that another 
     * binding needs to be created with the source and destination reversed.  
     * This binding couldn't be created until the component registered it's model 
     * which sets the id.
     */
    private void completeTwoWayBindings() {
        // A side-effect of creating a new BindingExpression is that it is inserted
        // into the bindingExressions list.  Since we can't add to the list while 
        // iterating thru it, use an array instead.
        Object[] bindingExpressionsArray = bindingExpressions.toArray();
        for (int i = 0; i < bindingExpressionsArray.length; i++) {
            BindingExpression bindingExpression = (BindingExpression) bindingExpressionsArray[i];
            if (bindingExpression.isTwoWayPrimary()) {
                Model destination = bindingExpression.getDestination();

                if (destination != null) {
                    destination.ensureBindable();
                }

                String source2 = bindingExpression.getDestinationPath(false);

                BindingExpression bindingExpression2 = new BindingExpression(source2,
                        bindingExpression.getXmlLineNumber(), this);

                String destination2 = TextGen.stripParens(bindingExpression.getSourceExpression());
                bindingExpression2.setDestinationProperty(destination2);
                bindingExpression2.setDestinationLValue(destination2);
            }
        }
    }

    /**
     * Combine all the binding expression namespaces using the Integer as the 
     * unique key since the namespace int generation is shared across XML and XMLList
     * objects.
     * @return
     */
    public Map<Integer, String> getAllBindingNamespaces() {
        Map<Integer, String> allNs = new HashMap<Integer, String>();

        for (BindingExpression be : bindingExpressions) {
            if (be.getNamespaces() != null) {
                allNs.putAll(be.getNamespaces());
            }
        }

        return allNs;
    }

    public boolean hasBindingTags() {
        boolean result = false;

        for (BindingExpression bindingExpression : getBindingExpressions()) {
            if (bindingExpression.getDestination() == null) {
                result = true;
                break;
            }
        }

        return result;
    }

    /**
     * Called by ClassDefLib.vm to generate the var declarations for all the     
     * namespaces used by all the binding expressions.     
     */
    public String getAllBindingNamespaceDeclarations() {
        return BindingExpression.getNamespaceDeclarations(getAllBindingNamespaces());
    }

    /**
     *
     */
    public void postProcessStates() {
        statesModel.applyMetadata();
        if (statesModel.processReparents()) {
            statesModel.processStatefulModels();
            statesModel.setInitialState();
        }
    }

    /**
     *
     */
    public String getInterfaceList() {
        List<String> names = new ArrayList<String>(info.getInterfaceNames().size());
        for (Iterator i = info.getInterfaceNames().iterator(); i.hasNext();) {
            names.add(((DocumentInfo.NameInfo) i.next()).getName());
        }
        return TextGen.toCommaList(names.iterator());
    }

    public Set<DocumentInfo.NameInfo> getInterfaceNames() {
        return info.getInterfaceNames();
    }

    /**
     *
     */
    public String getWatcherSetupUtilClassName() {
        StringBuilder stringBuffer = new StringBuilder("_");

        String packageName = getPackageName();

        if ((packageName != null) && (packageName.length() > 0)) {
            stringBuffer.append(packageName.replace('.', '_'));
            stringBuffer.append("_");
        }

        stringBuffer.append(getClassName());
        stringBuffer.append("WatcherSetupUtil");

        return stringBuffer.toString();
    }

    /**
     *
     */
    public CodeFragmentList getDescriptorDeclaration(String name) {
        CodeFragmentList fragList = new CodeFragmentList();

        DescriptorGenerator.addDescriptorInitializerFragments(fragList, getRoot(),
                FrameworkDefs.requiredTopLevelDescriptorProperties, true, "");

        fragList.add(0, "private var " + name + " : "
                + NameFormatter.toDot(standardDefs.CLASS_UICOMPONENTDESCRIPTOR) + " = ", 0);

        return fragList;
    }

    /**
     * Generates all code necessary to maintain our stateful model.
     */
    public CodeFragmentList getStatesDeclaration() {
        StatesGenerator generator = new StatesGenerator(standardDefs);
        return (getVersion() >= 4) ? generator.getStatesInitializerFragments(statesModel) : new CodeFragmentList();
    }

    /**
     * If an inherited property by the given name exists, we check usage constraints.
     * @return true iff inherited property exists, and an assignment to it (under the given type) is an error.
     */
    private final boolean inheritedPropertyUsageError(String name, Type type, int line) {
        assert root != null : "root null in checkInherited";

        Property prop = root.getType().getProperty(name);

        if (prop != null) {
            if (!prop.hasPublic()) {
                ThreadLocalToolkit.log(new NonPublicInheritedPropertyInit(name), getSourcePath(), line);
                return true;
            }

            if (prop.readOnly()) {
                ThreadLocalToolkit.log(new ReadOnlyInheritedPropertyInit(name), getSourcePath(), line);
                return true;
            }

            if (!type.isAssignableTo(prop.getType())) {
                ThreadLocalToolkit.log(
                        new TypeIncompatibleInheritedPropertyInit(name,
                                NameFormatter.toDot(prop.getType().getName()), NameFormatter.toDot(type.getName())),
                        getSourcePath(), line);

                return true;
            }
        }

        return false;
    }

    /*
     * Validates a set of states against our states list to ensure previous 
     * declaration.
     */
    private final boolean unresolvedStateIdentifier(Collection states, int line) {
        for (Iterator iter = states.iterator(); iter.hasNext();) {
            String state = (String) iter.next();
            if (!validateState(state, line))
                return true;
        }
        return false;
    }

    /*
     * Validates a state against our explicit states list, generate error if
     * not declared.
     */
    public boolean validateState(String state, int line) {
        if (!statesModel.validateState(state)) {
            ThreadLocalToolkit.log(new StateResolutionError(state), getSourcePath(), line);
            return false;
        }
        return true;
    }

    /*
     * Validates either an include or exclude state list is specified, (but not
     * both).  Ensures states are valid within specified filter.
     */
    public boolean validateStateFilters(Collection include, Collection exclude, int line) {
        if (!include.isEmpty() && !exclude.isEmpty()) {
            ThreadLocalToolkit.log(new AmbiguousStateFilterError(), getSourcePath(), line);
            return false;
        }

        if (unresolvedStateIdentifier(include, line) || unresolvedStateIdentifier(exclude, line)) {
            return false;
        }

        return true;
    }

    /**
     * Collection of declared DesignLayer instances that wouldn't otherwise
     * be associated with any layer children. These eventually will become
     * top level declarations.
     */
    public List<DesignLayerNode> getLayerDeclarationNodes() {
        return info.getRootNode().layerDeclarationNodes;
    }

    /**
     * delegate to FrameworkDefs for names of generated management vars
     */
    public static List<VariableDeclaration> getBindingManagementVars() {
        return FrameworkDefs.bindingManagementVars;
    }

    /*
     * Register a state specific node with our states model.
     */
    public void registerStateSpecificNode(Model model, Node node, Collection<String> includedStates,
            Collection<String> excludedStates) {
        // Validate and then register this stateful node with our states builder.    
        if (validateStateFilters(includedStates, excludedStates, model.getXmlLineNumber())) {
            statesModel.registerStateSpecificNode(model, node, includedStates, excludedStates);
        }
    }

    /*
     * Register a state specific property with our states model.
     */
    public void registerStateSpecificProperty(Model model, String property, ValueInitializer value,
            String stateName) {
        statesModel.registerStateSpecificProperty(model, property, value, stateName);
    }

    /*
     * Register a state specific style with our states model.
     */
    public void registerStateSpecificStyle(Model model, String property, ValueInitializer value, String stateName) {
        statesModel.registerStateSpecificStyle(model, property, value, stateName);
    }

    /*
     * Register a state specific event handler with our states model.
     */
    public void registerStateSpecificEventHandler(Model model, String event, EventInitializer value,
            String stateName) {
        statesModel.registerStateSpecificEventHandler(model, event, value, stateName);
    }

    /*
     * Register a realized state model so any event initializers, etc. can be accounted for.
     */
    public void registerState(Model model, Node node) {
        statesModel.registerState(model, node);
    }

    /*
     * Returns our stateful document model.
     */
    public StatesModel getStatefulModel() {
        return statesModel;
    }

    /*
     * Denote that a state-specific document node should be instantiated early.
     */
    public void registerEarlyInitNode(Model model) {
        statesModel.registerEarlyInitNode(model);
    }

    /**
     * CompilerErrors
     */
    public static class NonPublicInheritedPropertyInit extends CompilerMessage.CompilerError {
        private static final long serialVersionUID = 9044603625972071302L;
        public String name;

        public NonPublicInheritedPropertyInit(String name) {
            this.name = name;
        }
    }

    public static class ReadOnlyInheritedPropertyInit extends CompilerMessage.CompilerError {
        private static final long serialVersionUID = -2959436790426946620L;
        public String name;

        public ReadOnlyInheritedPropertyInit(String name) {
            this.name = name;
        }
    }

    public static class TypeIncompatibleInheritedPropertyInit extends CompilerMessage.CompilerError {
        private static final long serialVersionUID = -6205552750667618804L;
        public String name, propertyType, valueType;

        public TypeIncompatibleInheritedPropertyInit(String name, String propertyType, String valueType) {
            this.name = name;
            this.propertyType = propertyType;
            this.valueType = valueType;
        }
    }

    public static class StateResolutionError extends CompilerMessage.CompilerError {
        private static final long serialVersionUID = -7520940017001772178L;
        public String name;

        public StateResolutionError(String name) {
            this.name = name;
        }
    }

    public static class AmbiguousStateFilterError extends CompilerMessage.CompilerError {
        private static final long serialVersionUID = 6642005101532059046L;

        public AmbiguousStateFilterError() {
        }
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }
}