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

Java tutorial

Introduction

Here is the source code for flex2.compiler.mxml.rep.Model.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 flex2.compiler.mxml.gen.CodeFragmentList;
import flex2.compiler.mxml.gen.TextGen;
import flex2.compiler.mxml.lang.StandardDefs;
import flex2.compiler.mxml.reflect.*;
import flex2.compiler.mxml.rep.init.*;
import flex2.compiler.util.IteratorList;
import flex2.compiler.util.NameFormatter;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.iterators.FilterIterator;
import org.apache.commons.collections.iterators.IteratorChain;
import org.apache.commons.collections.iterators.TransformIterator;

import java.util.*;

/**
 * This class represents a non-language node in a MXML document.
 */
public class Model implements LineNumberMapped {
    private Type type;

    /**
     * line number where this model occurred in xml.  -1 if this model
     * is synthetic and has no creation site in MXML.
     */
    private int xmlLineNumber;

    /**
     * this is the guaranteed-unique name used by the value initializer
     * TODO see below
     */
    private String definitionName;

    /**
     * this is the ID we have given this instance in mxml.  it will either be user assigned or
     * assigned automatically by the compiler.
     * NOTE: in anonymous cases (e.g. XML), this may be a child element name, thus not guaranteed unique.
     */
    // TODO id should be the guaranteed-unique one; childName or something for the other one.
    private String id;

    /**
     * this flag is true if the id (see above) was assigned automatically
     * by the compiler.  Conversely, this flag is false if the user
     * explicitly assigned the id
     */
    private boolean idIsAutogenerated = false;

    /**
     * indicates whether this Model is a child of an AnonymousObjectGraph
     */
    private boolean isAnonymous = false;

    private MxmlDocument document;
    protected final StandardDefs standardDefs;

    private Model parent;
    private String parentIndex;
    private String parentIndexState;

    private boolean inspectable = false;

    private List<Model> repeaterParents;

    private Map<String, Initializer> properties;
    private Map<String, Initializer> styles;
    private Map<String, Initializer> effects;
    private Map<String, Initializer> events;
    private Collection<String> states;

    /*
     * The DesignLayer associated with this model.
     */
    public DesignLayer layerParent;

    private boolean described; //  TODO remove when DI is generalized

    /*
     * Denotes whether or not this node is state-specific.
     */
    private boolean stateSpecific = false;

    /*
     * Denotes whether or not this node is to be instantiated immediately
     * upon document instantiation (runtime).
     */
    public boolean earlyInit = false;

    /*
     * used to force declare a model, even when not normally applicable.
     */
    private boolean ensureDeclaration = false;

    /*
     * used to force a models property declaration to be bindable.
     */
    private boolean ensureBindable = false;

    /*
     * indicates whether this Model should be initialized within the document descriptor.
     */
    private boolean descriptorInit = true;

    /*
     * indicates that this model serves as the rvalue of an ITransientDeferredInstance type.
     */
    private boolean isTransient = false;

    public Model(MxmlDocument document, Type type, int line) {
        this(document, type, null, line);
    }

    public Model(MxmlDocument document, Type type, Model parent, int line) {
        assert (type != null);

        this.document = document;
        this.standardDefs = document.getStandardDefs();
        this.type = type;
        this.parent = parent;
        setXmlLineNumber(line);

        document.ensureId(this);
        setDefinitionName(getId());
    }

    public final boolean isDeclared() {
        //  TODO first clause is necessary due to id being abused in AOG case (see AOGBuilder ~line 85.
        //  This breaks meaning of id - need to find another way to enable correct databinding codegen in AOG case.
        return !isAnonymous && document.isDeclared(this);
    }

    public final StandardDefs getStandardDefs() {
        return standardDefs;
    }

    public final Type getType() {
        return type;
    }

    public final String getDefinitionName() {
        return definitionName;
    }

    public final void setDefinitionName(String definitionName) {
        this.definitionName = definitionName;
    }

    public final String getId() {
        return id;
    }

    public final void setId(String id, boolean idIsAutogenerated) {
        this.id = id;
        this.idIsAutogenerated = idIsAutogenerated;
    }

    public final boolean getIdIsAutogenerated() {
        return idIsAutogenerated;
    }

    public final void ensureBindable() {
        ensureBindable = true;
    }

    public final boolean getBindabilityEnsured() {
        return ensureBindable;
    }

    public final boolean getIsAnonymous() {
        return isAnonymous;
    }

    public final void setIsAnonymous(boolean isAnonymous) {
        this.isAnonymous = isAnonymous;
    }

    public final boolean getIsTransient() {
        return isTransient;
    }

    public final void setIsTransient(boolean isTransient) {
        this.isTransient = isTransient;
    }

    public final MxmlDocument getDocument() {
        return document;
    }

    public final void setParent(Model parent) {
        this.parent = parent;
    }

    public final Model getParent() {
        return parent;
    }

    public final void setParentIndex(String index) {
        this.parentIndex = index;
    }

    public final void setParentIndex(String index, String state) {
        this.parentIndex = index;
        this.parentIndexState = state;
    }

    public final void setParentIndex(int index) {
        this.parentIndex = Integer.toString(index);
    }

    public final String getParentIndex() {
        return parentIndex;
    }

    public final String getParentIndexState() {
        return parentIndexState;
    }

    public final int getXmlLineNumber() {
        return xmlLineNumber;
    }

    public final void setXmlLineNumber(int xmlLineNumber) {
        this.xmlLineNumber = xmlLineNumber;
    }

    public final boolean getInspectable() {
        return inspectable;
    }

    public final void setInspectable(boolean inspectable) {
        this.inspectable = inspectable;
    }

    public final int getRepeaterLevel() {
        return getRepeaterParents().size();
    }

    public final List<Model> getRepeaterParents() {
        if (repeaterParents == null) {
            repeaterParents = new ArrayList<Model>();

            if (parent != null) {
                repeaterParents.addAll(parent.getRepeaterParents());

                if (standardDefs.isRepeater(parent.getType())) {
                    repeaterParents.add(parent);
                }
            }
        }

        return repeaterParents;
    }

    /**
     *
     */
    public void setProperty(String name, Model value) {
        setProperty(name, value, value.getXmlLineNumber());
    }

    /**
     *
     */
    public void setProperty(Property property, Object value, int line) {
        ValueInitializer initializer = new StaticPropertyInitializer(property, value, line, standardDefs);

        // Register this property and value as pertaining only to a specific state, otherwise
        // assign to this instance (base state).
        if (property.isStateSpecific()) {
            document.registerStateSpecificProperty(this, property.getName(), initializer, property.getStateName());
        } else {
            (properties != null ? properties : (properties = new LinkedHashMap<String, Initializer>()))
                    .put(property.getName(), initializer);
        }
    }

    /**
     *
     */
    public void setDynamicProperty(Type type, String name, Object value, String state, int line) {
        ValueInitializer initializer = new DynamicPropertyInitializer(type, name, value, line, standardDefs);

        // Register this property and value as pertaining only to a specific state, otherwise
        // assign to this instance (base state).
        if (state != null) {
            document.registerStateSpecificProperty(this, name, initializer, state);
        } else {
            (properties != null ? properties : (properties = new LinkedHashMap<String, Initializer>())).put(name,
                    initializer);
        }
    }

    /**
     * TODO legacy shim. Convert all callers to either setProperty(property, value) or setDynamicProperty(type, name, value)
     */
    public void setProperty(String name, Object value, int line) {
        Property property = type.getProperty(name);
        if (property != null) {
            setProperty(property, value, line);
        } else {
            setDynamicProperty(type.getTypeTable().objectType, name, value, null, line);
        }
    }

    /**
     *
     */
    public final boolean hasProperty(String name) {
        return getProperties().containsKey(name);
    }

    /**
     *
     */
    public boolean hasBindings() {
        return bindingsOnly(getProperties().values().iterator()).hasNext()
                || bindingsOnly(getStyles().values().iterator()).hasNext()
                || bindingsOnly(getEffects().values().iterator()).hasNext();
    }

    /**
     * Returns true if the specified property is the target of a binding.
     */
    public boolean hasDataBoundProperty(String name) {
        Initializer initializer = (Initializer) getProperties().get(name);
        return initializer != null ? initializer.isBinding() : false;
    }

    /**
     * Returns true if the specified style is the target of a binding.
     */
    public boolean hasDataBoundStyle(String name) {
        Initializer initializer = (Initializer) getStyles().get(name);
        return initializer != null ? initializer.isBinding() : false;
    }

    /**
     * Returns true if the specified event property is the target of a binding.
     */
    public boolean hasDataBoundEvent(String name) {
        Initializer initializer = (Initializer) getEvents().get(name);
        return initializer != null ? initializer.isBinding() : false;
    }

    /**
     * Returns true if the specified effect property is the target of a binding.
     */
    public boolean hasDataBoundEffect(String name) {
        Initializer initializer = (Initializer) getEffects().get(name);
        return initializer != null ? initializer.isBinding() : false;
    }

    /**
     * Note that we do *not* filter out bindings by default for property initializers.
     */
    public final Iterator<Initializer> getPropertyInitializerIterator() {
        return getPropertyInitializerIterator(true);
    }

    /**
     *
     */
    public final Iterator<Initializer> getPropertyInitializerIterator(boolean includeBindings) {
        return includeBindings ? getProperties().values().iterator()
                : excludeBindings(getProperties().values().iterator());
    }

    /**
     *
     */
    public boolean isEmpty() {
        return properties == null && styles == null && effects == null && events == null;
    }

    /**
     * TODO make this private once RemoteObjectBuilder usage has been removed
     */
    public final Map<String, Initializer> getProperties() {
        return properties != null ? properties : Collections.<String, Initializer>emptyMap();
    }

    /**
     * TODO legacy - delete once AnonymousObjectGraphBuilder usage has been removed
     */
    public Object getProperty(String name) {
        ValueInitializer initializer = (ValueInitializer) getProperties().get(name);
        return initializer != null ? initializer.getValue() : null;
    }

    /**
     *
     */
    public final void setStyle(String name, Object value, int line) {
        Style style = type.getStyle(name);
        assert style != null : "style '" + name + "' not defined on type '" + type.getName() + "'";
        setStyle(style, value, line);
    }

    /**
     *
     */
    public final void setStyle(Style style, Object value, int line) {
        StyleInitializer styleInitializer = new StyleInitializer(style, value, line, standardDefs);

        // Register this style and value as pertaining only to a specific state, otherwise
        // assign to this instance (base state).
        if (style.isStateSpecific()) {
            document.registerStateSpecificStyle(this, style.getName(), styleInitializer, style.getStateName());
        } else {
            (styles != null ? styles : (styles = new LinkedHashMap<String, Initializer>())).put(style.getName(),
                    styleInitializer);
        }
    }

    public final Iterator<Initializer> getStyleInitializerIterator() {
        return excludeBindings(getStyles().values().iterator());
    }

    public final boolean hasStyle(String name) {
        return getStyles().containsKey(name);
    }

    private Map<String, Initializer> getStyles() {
        return styles != null ? styles : Collections.<String, Initializer>emptyMap();
    }

    /**
     * Note: this is a little irregular; effect rvalues are either class names or bindings.
     */
    public final void setEffect(String name, Object value, Type effectType, int line) {
        Effect effect = type.getEffect(name);
        setEffect(effect, value, effectType, line);
    }

    /**
     * Note: this is a little irregular; effect rvalues are either class names or bindings.
     */
    public final void setEffect(Effect effect, Object value, Type effectType, int line) {
        EffectInitializer effectInitializer = new EffectInitializer(effect, value, effectType, line, standardDefs);

        // Register this effect and value as pertaining only to a specific state, otherwise
        // assign to this instance (base state).
        if (effect.isStateSpecific()) {
            document.registerStateSpecificStyle(this, effect.getName(), effectInitializer, effect.getStateName());
        } else {
            (effects != null ? effects : (effects = new LinkedHashMap<String, Initializer>())).put(effect.getName(),
                    effectInitializer);
        }
    }

    public final Iterator<Initializer> getEffectInitializerIterator() {
        return excludeBindings(getEffects().values().iterator());
    }

    public final boolean hasEffect(String name) {
        return getEffects().containsKey(name);
    }

    public final Map<String, Initializer> getEffects() {
        return effects != null ? effects : Collections.<String, Initializer>emptyMap();
    }

    public String getEffectNames() {
        Iterator eventNameIter = new TransformIterator(getEffectInitializerIterator(), new Transformer() {
            public Object transform(Object object) {
                return TextGen.quoteWord(((EffectInitializer) object).getName());
            }
        });

        return TextGen.toCommaList(eventNameIter);
    }

    public String getEffectEventNames() {
        Iterator eventNameIter = new TransformIterator(getEffectInitializerIterator(), new Transformer() {
            public Object transform(Object object) {
                return TextGen.quoteWord(((EffectInitializer) object).getEventName());
            }
        });

        return TextGen.toCommaList(eventNameIter);
    }

    /**
     *
     */
    public Iterator getStyleAndEffectInitializerIterator() {
        return new IteratorChain(getStyleInitializerIterator(), getEffectInitializerIterator());
    }

    /**
     *
     */
    public final void setEvent(Event event, String text, int line) {
        EventHandler handler;

        // If this handler will be only assigned for a state other than base (default),
        // ensure we generate a corresponding import.
        if (!event.isStateSpecific()) {
            document.addImport(NameFormatter.toDot(event.getType().getName()), line);
            handler = new EventHandler(this, event, text);
        } else {
            handler = new EventHandler(this, event, text, event.getStateName());
        }

        handler.setXmlLineNumber(line);
        EventInitializer eventInitializer = new EventInitializer(handler);

        // Register this handler and value as pertaining only to a specific state, otherwise
        // assign to this instance (base state).
        if (event.isStateSpecific()) {
            document.addStateSpecificEventInitializer(eventInitializer);
            document.registerStateSpecificEventHandler(this, event.getName(), eventInitializer,
                    event.getStateName());
        } else {
            (events != null ? events : (events = new LinkedHashMap<String, Initializer>())).put(event.getName(),
                    eventInitializer);
        }
    }

    public final Iterator<Initializer> getEventInitializerIterator() {
        return getEvents().values().iterator();
    }

    public final boolean hasEvent(String name) {
        return getEvents().containsKey(name);
    }

    public final Initializer getEventInitializer(String name) {
        return getEvents().get(name);
    }

    private final Map<String, Initializer> getEvents() {
        return events != null ? events : Collections.<String, Initializer>emptyMap();
    }

    /**
     *  iterator containing definitions from our initializers
     */
    public Iterator<CodeFragmentList> getSubDefinitionsIterator() {
        IteratorList iterList = new IteratorList();

        addDefinitionIterators(iterList, getPropertyInitializerIterator());
        addDefinitionIterators(iterList, getStyleInitializerIterator());
        addDefinitionIterators(iterList, getEffectInitializerIterator());
        addDefinitionIterators(iterList, getEventInitializerIterator());

        return iterList.toIterator();
    }

    /**
     *
     */
    protected static void addDefinitionIterators(IteratorList iterList, Iterator<? extends Initializer> initIter) {
        while (initIter.hasNext()) {
            iterList.add(initIter.next().getDefinitionsIterator());
        }
    }

    /**
     *  iterator containing our initializers
     */
    public Iterator<Initializer> getSubInitializerIterator() {
        IteratorList iterList = new IteratorList();

        iterList.add(getPropertyInitializerIterator());
        iterList.add(getStyleInitializerIterator());
        iterList.add(getEffectInitializerIterator());
        iterList.add(getEventInitializerIterator());

        return iterList.toIterator();
    }

    /**
     *
     */
    protected Iterator<Initializer> excludeBindings(Iterator<? extends Initializer> iter) {
        return bindingFilter(iter, false);
    }

    /**
     *
     */
    protected Iterator<Initializer> bindingsOnly(Iterator<? extends Initializer> iter) {
        return bindingFilter(iter, true);
    }

    /**
     *
     */
    @SuppressWarnings("unchecked")
    protected Iterator<Initializer> bindingFilter(Iterator<? extends Initializer> iter, final boolean include) {
        return new FilterIterator(iter, new Predicate() {
            public boolean evaluate(Object object) {
                return (((Initializer) object).isBinding()) == include;
            }
        });
    }

    /**
     *
     */
    public final void setDescribed(boolean described) {
        this.described = described;
    }

    /**
     *
     */
    public final boolean isDescribed() {
        return described;
    }

    /**
     * Denotes whether or not this node is state-specific.
     */
    public final void setStateSpecific(boolean stateful) {
        this.stateSpecific = stateful;
    }

    /**
     * Denotes whether or not this node is state-specific.
     */
    public final boolean isStateSpecific() {
        return stateSpecific;
    }

    /**
     * Denotes whether or not this node is to be created immediate
     * upon document initialization (runtime).
     */
    public final void setEarlyInit(boolean earlyInit) {
        this.earlyInit = earlyInit;
    }

    /**
     * Denotes whether or not this node is to be created immediate
     * upon document initialization (runtime).
     */
    public final boolean isEarlyInit() {
        return earlyInit;
    }

    /**
     * Sets the list of states which this node is to apply.
     */
    public final void setStates(Collection<String> states) {
        this.states = states;
    }

    /**
     * Returns list of states for which this node applies.
     */
    public final Collection<String> getStates() {
        return states;
    }

    /**
     * Returns true if this object is to be applied for a given state.
     */
    public final boolean hasState(String state) {
        if (states != null) {
            return states.contains(state);
        }
        return false;
    }

    /**
     * used to force declare a model, even when not normally applicable.
     */
    public final void ensureDeclaration() {
        this.ensureDeclaration = true;
    }

    /**
     * used to force declare a model, even when not normally applicable.
     */
    public final boolean isDeclarationEnsured() {
        return ensureDeclaration;
    }

    /**
     * indicates whether this Model should be initialized within the document descriptor.
     */
    public final void setDescriptorInit(boolean descriptorInit) {
        this.descriptorInit = descriptorInit;
    }

    /**
     * indicates whether this Model should be initialized within the document descriptor.
     */
    public final boolean isDescriptorInit() {
        return descriptorInit;
    }

    /**
     * comment field for asdoc generation.
     */
    public String comment;
}