Package flex2.compiler.mxml.builder

Source Code of flex2.compiler.mxml.builder.XMLListBuilder$RequireXMLContent

/*
*
*  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.builder;

import java.io.StringWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import macromedia.asc.util.IntegerPool;

import flex2.compiler.CompilationUnit;
import flex2.compiler.util.CompilerMessage.CompilerError;
import flex2.compiler.mxml.MxmlConfiguration;
import flex2.compiler.mxml.dom.CDATANode;
import flex2.compiler.mxml.dom.Node;
import flex2.compiler.mxml.dom.XMLListNode;
import flex2.compiler.mxml.lang.StandardDefs;
import flex2.compiler.mxml.lang.TextParser;
import flex2.compiler.mxml.reflect.TypeTable;
import flex2.compiler.mxml.rep.AtResource;
import flex2.compiler.mxml.rep.BindingExpression;
import flex2.compiler.mxml.rep.Model;
import flex2.compiler.mxml.rep.MxmlDocument;
import flex2.compiler.mxml.rep.XMLList;
import flex2.compiler.util.PrefixMapping;
import flex2.compiler.util.QName;
import flex2.compiler.util.QNameMap;

/**
* This builder handles building an XMLList instance from an
* XMLListNode and it's children.
*/
public class XMLListBuilder extends AbstractBuilder
{

    XMLListBuilder(CompilationUnit unit, TypeTable typeTable, MxmlConfiguration mxmlConfiguration, MxmlDocument document)
    {
        this(unit, typeTable, mxmlConfiguration, document, null);
        allowTopLevelBinding = true;
    }

    XMLListBuilder(CompilationUnit unit, TypeTable typeTable, MxmlConfiguration mxmlConfiguration, MxmlDocument document, Model parent)
    {
        super(unit, typeTable, mxmlConfiguration, document);
        this.parent = parent;
        allowTopLevelBinding = false;
    }

    private String id;
    private Model parent;
    private boolean allowTopLevelBinding;
    XMLList xmlList;

    public void analyze(XMLListNode node)
    {
        id = (String)getLanguageAttributeValue(node, StandardDefs.PROP_ID);
        xmlList = new XMLList(document, typeTable.xmlListType, parent, node.beginLine);

        if (id != null)
        {
            xmlList.setId(id, false);
        }

        StringWriter writer = new StringWriter();

        if (node.getChildCount() == 1 && node.getChildAt(0) instanceof CDATANode)
        {
            CDATANode cdata = (CDATANode) node.getChildAt(0);

            if (cdata.image.length() > 0)
            {
                BindingExpression be = textParser.parseBindingExpression(cdata.image, cdata.beginLine);

                if (be != null)
                {
                    if (allowTopLevelBinding)
                    {
                        be.setDestination(xmlList);
                        writer.write("null");
                    }
                    else
                    {
                        log(cdata, new AbstractBuilder.BindingNotAllowed());                       
                    }                   
                }
                else
                {
                    log(node, new RequireXMLContent());
                }
            }
        }
        else
        {
            processChildren(node, writer, null, new Stack<String>(), new Stack<PrefixMapping>());
        }

        xmlList.setLiteralXML(writer.toString());
    }

    /**
     *
     */
    private void processNode(Node node,
                             StringWriter serializer,
                             String e4xElementsByLocalName,
                             Stack<String> destinationPropertyStack,
                             Stack<PrefixMapping> namespaces)
    {
        //System.out.println("processChildren: " + e4xElementsByLocalName + " " + createExpression(destinationPropertyStack));
       
        QNameMap<BindingExpression> attributeBindings = processBindingAttributes(node);
        processResourceAttributes(node);
       
        if (attributeBindings != null)
        {
            //[Matt] I think this value is wrong but I can't see where it's used so we'll leave it for now.
            String destinationProperty = createExpression(destinationPropertyStack);

            for (Iterator<QName> i = attributeBindings.keySet().iterator(); i.hasNext();)
            {
                QName attrName = i.next();
               
                String attrExpr;
                int nsId = 0;
               
                // If the attribute node has a namespace use that.  Otherwise
                // use the namespace of the element node.
                String nsUri = attrName.getNamespace();
                if (nsUri.length() > 0)
                {
                    nsId = PrefixMapping.getNamespaceId(nsUri, namespaces);                       
                }
                else
                {
                    PrefixMapping pm = (PrefixMapping) namespaces.peek();
                    nsUri = pm.getUri();
                    nsId = pm.getNs();
                }
               
                if (nsId > 0)
                {
                    attrExpr = e4xElementsByLocalName + ".@ns" + nsId + "::" + attrName.getLocalPart();
                }
                else
                {
                    attrExpr = e4xElementsByLocalName + ".@" + attrName.getLocalPart();
                }

                BindingExpression be = attributeBindings.get(attrName);
               
                be.setDestinationLValue(attrExpr);
                be.setDestinationProperty(destinationProperty + "[" + node.getIndex() + "]");
                be.setDestination(xmlList);
                be.setDestinationXMLAttribute(true);
               
                // So be.getDestination() will use LValue.  The destinationProperty is wrong.               
                be.setDestinationE4X(true);
               
                //System.out.println("cdata: LValue=" + e4xElementsByLocalName + " prop=" + be.getDestinationProperty()));

                xmlList.setHasBindings(true);

                PrefixMapping.pushNamespaces(be, namespaces);
                if (nsUri.length() > 0)
                {
                    be.addNamespace(nsUri, nsId);
                }
            }
        }

        node.toStartElement(serializer);

        if (node.getChildCount() == 1 && node.getChildAt(0) instanceof CDATANode)
        {
            CDATANode cdata = (CDATANode) node.getChildAt(0);
            if (cdata.image.length() > 0)
            {
                BindingExpression be = textParser.parseBindingExpression(cdata.image, cdata.beginLine);
                if (be != null)
                {
                    //[Matt] I think this value is wrong but I can't see where it's used so we'll leave it for now.
                    String destinationProperty = createExpression(destinationPropertyStack);

                    be.setDestinationLValue(e4xElementsByLocalName);
                    be.setDestinationProperty(destinationProperty + "[" + node.getIndex() + "]");
                    be.setDestination(xmlList);
                    be.setDestinationXMLNode(true);
                   
                    // So be.getDestination() will use LValue.  The destinationProperty is wrong.
                    be.setDestinationE4X(true);

                    //System.out.println("cdata: LValue=" + e4xElementsByLocalName + " prop=" + be.getDestinationProperty()));
                   
                    xmlList.setHasBindings(true);

                    PrefixMapping.pushNamespaces(be, namespaces);
                }
                else
                {
                    String text = TextParser.cleanupBindingEscapes(cdata.image);
                    text = TextParser.cleanupAtFunctionEscapes(text);
                    serializer.write(text);
                }
            }
        }
        else
        {
            processChildren(node, serializer, e4xElementsByLocalName, destinationPropertyStack, namespaces);
        }

        node.toEndElement(serializer);
    }

    private void processChildren(Node node,
                                 StringWriter serializer,
                                 String e4xElementsByLocalName,
                                 Stack<String> destinationPropertyStack,
                                 Stack<PrefixMapping> namespaces)
    {
        //System.out.println("processChildren: " + e4xElementsByLocalName + " " + createExpression(destinationPropertyStack));
       
        assignIndices(node);

        for (int i = 0, count = node.getChildCount(); i < count; i++)
        {
            Node child = (Node) node.getChildAt(i);
            if (child instanceof CDATANode)
            {
                CDATANode cdata = (CDATANode) child;
                if (cdata.image.trim().length() > 0)
                {
                    // C: ignore CDATANode if other XML elements exist...
                    log(child, new XMLBuilder.MixedContent(child.image));
                }
                else
                {
                    // Whitespace is OK
                    serializer.write(cdata.image);
                }
            }
            else
            {
                PrefixMapping.pushNodeNamespace(child, namespaces);

                if (e4xElementsByLocalName != null)
                {
                    StringBuilder e4xbuffer = new StringBuilder(e4xElementsByLocalName);
                    String destProp = child.getLocalPart();
                    if (child.getNamespace().length() > 0)
                    {
                        PrefixMapping pm = namespaces.peek();
                        destProp = "ns" + pm.getNs() + "::" + destProp;
                    }
                    e4xbuffer.append(".").append(destProp).append("[").append(child.getIndex()).append("]");

                    destinationPropertyStack.push(destProp);
                    processNode(child, serializer, e4xbuffer.toString(), destinationPropertyStack, namespaces);
                    destinationPropertyStack.pop();
                }
                else
                {
                    StringBuilder e4xbuffer = new StringBuilder(xmlList.getId());
                    e4xbuffer.append("[").append(i).append("]");

                    destinationPropertyStack.push(child.getLocalPart());
                    processNode(child, serializer, e4xbuffer.toString(), destinationPropertyStack, namespaces);
                    destinationPropertyStack.pop();
                }

                PrefixMapping.popNodeNamespace(namespaces);
            }
        }
    }

    /**
     * Collects/processes Binding attributes from the node, and then removes them from the node.
     */
    private QNameMap<BindingExpression> processBindingAttributes(Node node)
    {
        QNameMap<BindingExpression> attributeBindings = null;

        for (Iterator<QName> i = node.getAttributeNames(); i != null && i.hasNext();)
        {
            QName qname = i.next();
            String value = (String) node.getAttributeValue(qname);
           
            BindingExpression be = textParser.parseBindingExpression(value, node.beginLine);

            if (be != null)
            {
                if (attributeBindings == null)
                {
                    attributeBindings = new QNameMap<BindingExpression>();
                }
                // C: only localPart as the key?
                attributeBindings.put(qname, be);
                i.remove();
            }
        }

        return attributeBindings;
    }
   
    /**
     * Processes @Resource attributes from the node.
     */
    // TODO need to do all sorts of error testing
    //         * e.g. invalid atFunctions
    //         * invalid Resource arguments
    //         * @Resource in CDATA (is this allowed?) (like databinding in CDATA?)
    private void processResourceAttributes(Node node)
    {
        final QNameMap<AtResource> attributeResources = new QNameMap<AtResource>();
       
        for (Iterator i = node.getAttributeNames(); i != null && i.hasNext();)
        {
            final QName qname = (QName)i.next();
            final String text = (String) node.getAttributeValue(qname);

            final String atFunction = TextParser.getAtFunctionName(text);
            if ("Resource".equals(atFunction))
            {
                //TODO I am assuming that this should always be a string type because this is
                //     XML, though @Resources allow things like Embed. I'm right?
                //TODO test an Embed and see what happens?
                final AtResource atResource
                    = (AtResource)textParser.resource(text, typeTable.stringType);
               
                if (atResource != null)
                {               
                    // C: only localPart as the key?
                    attributeResources.put(qname, atResource);
                   
                    // we don't remove these here since we don't want to reorder the attributes
                    // we also don't call addAttribute() to update the map to avoid a potential
                    // ConcurrentModificationException on the iterator
                    //i.remove();
                }
            }
            else if (atFunction != null)
            {
                // if we found an invalid @Function, throw an error
                textParser.desc = atFunction;
                textParser.line = node.beginLine;
                textParser.error(TextParser.ErrUnrecognizedAtFunction, null, null, null);
            }
        }
       
        // now update the definitions in the attribute map
        for(Iterator<QName> iter = attributeResources.keySet().iterator(); iter.hasNext();)
        {
            final QName qname = iter.next();
            final AtResource atResource = attributeResources.get(qname);
           
            // attributes are in a LinkedHashMap, so this just updates the existing mapping
            // with the qname -> AtResource. When Element.toStartElement() is emitting the
            // attribute's value, it will notice the special case of an AtResource object
            // (instead of String) and emit an E4X expression with braces rather than a
            // String with double-quotes.
            node.addAttribute(qname.getNamespace(), qname.getLocalPart(), atResource, node.beginLine);
        }
    }
   
    // C: The implementation of this method depends on the implementation of app model's
    //    NamespaceUtil.getElementsByLocalName()...
    private void assignIndices(Node parent)
    {
        Map<String, Integer> counts = new HashMap<String, Integer>();

        Integer zero = IntegerPool.getNumber(0);

        for (int i = 0, count = parent.getChildCount(); i < count; i++)
        {
            Node child = (Node) parent.getChildAt(i);
            if (child instanceof CDATANode)
            {
                continue;
            }

            if (!counts.containsKey(child.image))
            {
                counts.put(child.image, zero);
                child.setIndex(0);
            }
            else
            {
                int num = counts.get(child.image).intValue() + 1;
                counts.put(child.image, IntegerPool.getNumber(num));
                child.setIndex(num);
            }
        }
    }

    private String createExpression(Stack<String> stack)
    {
        StringBuilder buffer = new StringBuilder();

        for (int i = 0, count = stack.size(); i < count; i++)
        {
            buffer.append(stack.get(i));
            if (i < count - 1)
            {
                buffer.append(".");
            }
        }

        return buffer.toString();
    }

    public static class RequireXMLContent extends CompilerError
    {
        private static final long serialVersionUID = 6892112465291416940L;

        public RequireXMLContent()
        {
            super();
        }
    }
}
TOP

Related Classes of flex2.compiler.mxml.builder.XMLListBuilder$RequireXMLContent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.