Package flex2.compiler.mxml.dom

Source Code of flex2.compiler.mxml.dom.MxmlScanner$XMLTagAttributeLineNumber

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

import flex2.compiler.mxml.MXMLNamespaces;
import flex2.compiler.mxml.Parser;
import flex2.compiler.mxml.ParserConstants;
import flex2.compiler.mxml.Token;
import flex2.compiler.mxml.TokenManager;
import flex2.compiler.util.CompilerMessage;
import flex2.compiler.util.ThreadLocalToolkit;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static flex2.compiler.mxml.lang.StandardDefs.SPARK_TEXT_ATTRIBUTE;
import static flex2.compiler.mxml.lang.StandardDefs.SPARK_TEXT_CONTENT_PROPERTIES;
import static flex2.compiler.mxml.lang.StandardDefs.SPARK_TEXT_TAGS;

/**
* JavaCC-compatible token manager. It uses SAXParser to do MXML parsing.
*
* @author Clement Wong
*/
public class MxmlScanner extends DefaultHandler implements TokenManager,
        MXMLNamespaces
{
    private static final String CUSTOM_ATTRIBUTES_CLASS = "org.apache.xerces.parsers.AbstractSAXParserMMImpl$AttributesProxy";
    private static Class CustomAttributeClass = null;
    private static XercesClassLoader xercesClassLoader;

    public static String MarkupNotRecognizedInContent, ReservedPITarget,
            MarkupNotRecognizedInMisc, ETagRequired;

    static
    {
        try
        {
            // use reflection to determine if the Attributes implementation is
            // an instance of
            // the Flex Xerces modified class - don't directly use
            // instanceof because
            // we want to avoid having a hard dependency on that custom class,
            // or any xerces code,
            // so that MxmlParser.java can remain cross-platform wrt J2EE and
            // .NET
            ClassLoader contextClassLoader = getXercesClassLoader();
            CustomAttributeClass = Class.forName(CUSTOM_ATTRIBUTES_CLASS, true, contextClassLoader);
        }
        catch (Exception ex)
        {
            ThreadLocalToolkit.log(new XMLTagAttributeLineNumber());
        }

        java.util.ResourceBundle rb = null;
        try
        {
            rb = java.util.ResourceBundle.getBundle("org.apache.xerces.impl.msg.XMLMessages");
            if (rb != null)
            {
                MarkupNotRecognizedInContent = rb.getString("MarkupNotRecognizedInContent");
                ReservedPITarget = rb.getString("ReservedPITarget");
                MarkupNotRecognizedInMisc = rb.getString("MarkupNotRecognizedInMisc");
                ETagRequired = rb.getString("ETagRequired");
            }
        }
        catch (Exception ex)
        {
        }
        finally
        {
            if (rb == null)
            {
                MarkupNotRecognizedInContent = "The content of elements must consist of well-formed character data or markup.";
                ReservedPITarget = "The processing instruction target matching \"[xX][mM][lL]\" is not allowed.";
                MarkupNotRecognizedInMisc = "The markup in the document following the root element must be well-formed.";
                ETagRequired = "The element type \"{0}\" must be terminated by the matching end-tag \"</{0}>\".";
            }
        }
    }

    public MxmlScanner(InputStream in, boolean processDesignLayers)
    {
        this(in, processDesignLayers, false);
    }

    public MxmlScanner(InputStream in, boolean processDesignLayers, boolean processComments)
    {
        saxEvents = new ArrayList<Object>(100);
        elementStack = new Stack<Object>();
        cdataHandler = new CDATAHandler(processComments);
        this.processDesignLayers = processDesignLayers;
        pos = 0;
        ClassLoader cl = Thread.currentThread().getContextClassLoader();

        try
        {
            ClassLoader xercesClassLoader = getXercesClassLoader();
            Thread.currentThread().setContextClassLoader(xercesClassLoader);
            SAXParserFactory saxFactory = SAXParserFactory.newInstance();
            saxFactory.setValidating(false);
            saxFactory.setNamespaceAware(true);
            SAXParser parser = saxFactory.newSAXParser();
            parser.setProperty("http://xml.org/sax/properties/lexical-handler", cdataHandler);
            parser.parse(in, this);
        }
        catch (ParserConfigurationException ex)
        {
        }
        catch (SAXException ex)
        {
            Throwable t = ex.getCause();
            if (t instanceof ScannerError)
            {
                throw (ScannerError)t;
            }
        }
        catch (IOException ex)
        {
        }
        finally
        {
            Thread.currentThread().setContextClassLoader(cl);           
        }
    }

    private static ClassLoader getXercesClassLoader() throws IOException
    {
        if (xercesClassLoader == null)
        {
            URL url = MxmlScanner.class.getProtectionDomain().getCodeSource().getLocation();
            String urlString = url.toString();
            String base = urlString.substring(0, urlString.lastIndexOf("/"));
            URL xercesPatchJarURL = new URL(base + "/external/xercesPatch.jar");
            URL xercesImplJarURL = new URL(base + "/external/xercesImpl.jar");
            URL xercesImplJaJarURL = new URL(base + "/external/xercesImpl_ja.jar");
            URL[] classpath = new URL[] {xercesPatchJarURL, xercesImplJarURL, xercesImplJaJarURL};
            xercesClassLoader = new XercesClassLoader(classpath, MxmlScanner.class.getClassLoader());
        }

        return xercesClassLoader;
    }

    /**
     * saxEvents holds Tokens and ScannerErrors
     */
    private String languageNamespace;
    private int languageVersion;
    private List<Object> saxEvents;
    private Stack<Object> elementStack;
    private CDATAHandler cdataHandler;
    private int beginLine, beginColumn, pos, kind;
    private boolean processDesignLayers = false;

    /**
     * Implements the JavaCC TokenManager interface.
     *
     * @return
     * @throws ScannerError
     */
    public Token getNextToken() throws ScannerError
    {
        Object evt;

        if (pos >= saxEvents.size())
        {
            evt = saxEvents.get(saxEvents.size() - 1);

            int line, col;

            if (evt instanceof Token)
            {
                line = ((Token)evt).beginLine;
                col = ((Token)evt).beginColumn;

                String msg = ThreadLocalToolkit.getLocalizationManager().getLocalizedTextString(new UnexpectedEndOfTokenStream(((Token)evt).image));
                throw new ScannerError(line, col, msg);
            }
            else
            {
                line = ((ScannerError)evt).getLineNumber();
                col = ((ScannerError)evt).getColumnNumber();

                String msg = ThreadLocalToolkit.getLocalizationManager().getLocalizedTextString(new UnexpectedEndOfSAXStream(((ScannerError)evt).getReason()));
                throw new ScannerError(line, col, msg);
            }
        }

        evt = saxEvents.get(pos++);

        if (evt instanceof Token)
        {
            return (Token)evt;
        }
        else
        {
            throw (ScannerError)evt;
        }
    }

    /**
     * lexer state mgmt, to support legacy 1.x syntax
     * <p>
     * NOTE: this scheme works without a stack <strong>only as long as legacy
     * MXML subtrees do not nest</strong>.
     * <p>
     * E.g. at present, we support legacy syntax in subtrees rooted by Effect,
     * RemoteObject, WebService and HttpService, none of which may appear inside
     * any of the others.
     * <p>
     * If that constraint is violated, you'll need a stack to track lexer state,
     * at which point Scanner will resemble nothing so much as a standard SAX
     * handler-stack XML processor, sitting in front of what amounts to a
     * javacc-generated regex matcher in Parser. Once things get to that point,
     * we should move revisiting and simplifying the parser architecture to the
     * top of the list..
     */
    private static final int LEX_FLEX2 = 0;
    private static final int LEX_FLEX1_WEBSERVICE = 1;
    private static final int LEX_FLEX1_HTTPSERVICE = 2;
    private static final int LEX_FLEX1_REMOTEOBJECT = 3;
    private static final int LEX_FLEX2_PRIVATE = 4;
    private int state = LEX_FLEX2;
    private int textDepth;

    private int findElementType(String uri, String localName, boolean start)
    {
        int kind = Parser.findElementType(uri, localName, start);

        switch (state)
        {
            /**
             * Standard Flex 2 lex state. Transition into Flex1-compatible
             * top-level tags; suppress special Flex1 grandkids.
             */
            case LEX_FLEX2:
                switch (kind)
                {
                    case ParserConstants.START_WEBSERVICE:
                        state = LEX_FLEX1_WEBSERVICE;
                        break;
                    case ParserConstants.START_HTTPSERVICE:
                        state = LEX_FLEX1_HTTPSERVICE;
                        break;
                    case ParserConstants.START_REMOTEOBJECT:
                        state = LEX_FLEX1_REMOTEOBJECT;
                        break;
                    case ParserConstants.START_PRIVATE:
                        state = LEX_FLEX2_PRIVATE;
                        break;
                    case ParserConstants.START_OPERATION:
                    case ParserConstants.START_REQUEST:
                    case ParserConstants.START_METHOD:
                    case ParserConstants.START_ARGUMENTS:
                        kind = ParserConstants.START_ELEMENT;
                        break;
                    case ParserConstants.END_OPERATION:
                    case ParserConstants.END_REQUEST:
                    case ParserConstants.END_METHOD:
                    case ParserConstants.END_ARGUMENTS:
                        kind = ParserConstants.END_ELEMENT;
                        break;
                }
                break;

            /**
             * Ignore everything between the Private elements.
             */
            case LEX_FLEX2_PRIVATE:
                switch (kind)
                {
                    case ParserConstants.END_PRIVATE:
                        state = LEX_FLEX2;
                        break;
                    default:
                        kind = ParserConstants.SKIP_ELEMENT;
                        break;
                }
                break;

            /**
             * Flex 1 WebService lex state. Transition out on end tag, suppress
             * non-WS specialness.
             */
            case LEX_FLEX1_WEBSERVICE:
                switch (kind)
                {
                    case ParserConstants.END_WEBSERVICE:
                        state = LEX_FLEX2;
                        break;
                    case ParserConstants.START_METHOD:
                    case ParserConstants.START_ARGUMENTS:
                        kind = ParserConstants.START_ELEMENT;
                        break;
                    case ParserConstants.END_METHOD:
                    case ParserConstants.END_ARGUMENTS:
                        kind = ParserConstants.END_ELEMENT;
                        break;
                }
                break;

            /**
             * Flex 1 HTTPService lex state. Transition out on end tag, suppress
             * non-HTTPS specialness.
             */
            case LEX_FLEX1_HTTPSERVICE:
                switch (kind)
                {
                    case ParserConstants.END_HTTPSERVICE:
                        state = LEX_FLEX2;
                        break;
                    case ParserConstants.START_OPERATION:
                    case ParserConstants.START_METHOD:
                    case ParserConstants.START_ARGUMENTS:
                        kind = ParserConstants.START_ELEMENT;
                        break;
                    case ParserConstants.END_OPERATION:
                    case ParserConstants.END_METHOD:
                    case ParserConstants.END_ARGUMENTS:
                        kind = ParserConstants.END_ELEMENT;
                        break;
                }
                break;

            /**
             * Flex 1 RemoteObject lex state. Transition out on end tag,
             * suppress non-RO specialness.
             */
            case LEX_FLEX1_REMOTEOBJECT:
                switch (kind)
                {
                    case ParserConstants.END_REMOTEOBJECT:
                        state = LEX_FLEX2;
                        break;
                    case ParserConstants.START_OPERATION:
                    case ParserConstants.START_REQUEST:
                        kind = ParserConstants.START_ELEMENT;
                        break;
                    case ParserConstants.END_OPERATION:
                    case ParserConstants.END_REQUEST:
                        kind = ParserConstants.END_ELEMENT;
                        break;
                }
                break;
        }

        return kind;
    }

    protected Node createNode(int kind, String uri, String localName,
            Attributes attributes)
    {
        int numAttributes = attributes.getLength();
        Node node = null;

        switch (kind)
        {
            case ParserConstants.START_STYLE:
                node = new StyleNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_SCRIPT:
                node = new ScriptNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_METADATA:
                node = new MetaDataNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_MODEL:
                node = new ModelNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_XML:
                node = new XMLNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_XMLLIST:
                node = new XMLListNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_ARRAY:
                node = new ArrayNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_VECTOR:
                node = new VectorNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_STRING:
                node = new StringNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_NUMBER:
                node = new NumberNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_INT:
                node = new IntNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_UINT:
                node = new UIntNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_BOOLEAN:
                node = new BooleanNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_CLASS:
                node = new ClassNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_FUNCTION:
                node = new FunctionNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_WEBSERVICE:
                node = new WebServiceNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_HTTPSERVICE:
                node = new HTTPServiceNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_REMOTEOBJECT:
                node = new RemoteObjectNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_OPERATION:
                node = new OperationNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_REQUEST:
                if ("xml".equals(attributes.getValue("", "format")))
                {
                    node = new XMLNode(uri, localName, numAttributes);
                }
                else
                {
                    node = new RequestNode(uri, localName, numAttributes);
                }
                break;
            case ParserConstants.START_METHOD:
                node = new MethodNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_ARGUMENTS:
                node = new ArgumentsNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_BINDING:
                node = new BindingNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_ELEMENT:
                if (saxEvents.size() == 0)
                {
                    DocumentNode documentNode = new DocumentNode(uri, localName, numAttributes);
                    documentNode.setLanguageNamespace(languageNamespace);
                    documentNode.setVersion(languageVersion);
                    node = documentNode;
                }
                else
                {
                    // If the immediate parent of this node is a DesignLayer
                    // then we construct a LayeredNode and associate the layer
                    // parent if applicable.
                    Object parentElement = elementStack.peek();
                    if (processDesignLayers && parentElement instanceof DesignLayerNode)
                        node = new LayeredNode(uri, localName, numAttributes, (DesignLayerNode) parentElement);
                    else
                        node = new Node(uri, localName, numAttributes);
                }
                break;
            case ParserConstants.START_COMPONENT:
                node = new InlineComponentNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_DESIGNLAYER:
              // If immediate parent is a design layer, then ensure our
              // node is assigned a layerParent;
              Object parentElement = elementStack.peek();
              DesignLayerNode layerParent = (processDesignLayers && parentElement instanceof DesignLayerNode) ?
                  (DesignLayerNode) parentElement : null;
                node = new DesignLayerNode(uri, localName, numAttributes, layerParent);
                break;
            case ParserConstants.START_DECLARATIONS:
                node = new DeclarationsNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_REPARENT:
                node = new ReparentNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_STATE:
                node = new StateNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_PRIVATE:
                node = new PrivateNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_LIBRARY:
                node = new LibraryNode(uri, localName, numAttributes);
                break;
            case ParserConstants.START_DEFINITION:
                node = new DefinitionNode(uri, localName, numAttributes);
                break;
        }

        return node;
    }

    private void assignTokenPosition(Token t)
    {
        if (saxEvents.size() == 0)
        {
            beginLine = locator.getLineNumber();
            beginColumn = locator.getColumnNumber();
        }

        t.beginLine = beginLine;
        t.beginColumn = beginColumn;
        t.endLine = locator.getLineNumber();
        t.endColumn = locator.getColumnNumber();
    }

    public void close()
    {
        saxEvents.clear();
    }

    // override DefaultHandler

    private Locator locator;
    private Map<String, String> prefixMappings;

    public void setDocumentLocator(Locator locator)
    {
        this.locator = locator;
    }

    public void processingInstruction (String target, String data) throws SAXException
    {
        ThreadLocalToolkit.log(new UnexpectedProcessingInstruction(data));
    }
   
    public void startDocument() throws SAXException
    {
        prefixMappings = new HashMap<String, String>();
        textDepth = 0;
    }

    public void endDocument() throws SAXException
    {
        Token t = new Token();
        t.kind = ParserConstants.EOF;
        assignTokenPosition(t);
        t.image = "";
        saxEvents.add(t);
        kind = ParserConstants.EOF;

        locator = null;
        prefixMappings = null;
    }

    public void startPrefixMapping(String prefix, String uri)
            throws SAXException
    {
        prefixMappings.put(uri, prefix);

        int version = getLanguageVersion(uri);
        if (version != -1)
        {
            // Only one language version can be used per document.
            if (languageNamespace != null && !languageNamespace.equals(uri))
            {
                String msg = ThreadLocalToolkit.getLocalizationManager().getLocalizedTextString(new ConflictingLanguageVersion(languageNamespace, uri));
                throw new ScannerError(locator.getLineNumber(), locator.getColumnNumber(), msg);
            }

            languageNamespace = uri;
            languageVersion = version;
        }
    }

    public void endPrefixMapping(String prefix) throws SAXException
    {
        prefixMappings.remove(prefix);
    }

    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException
    {
        kind = findElementType(uri, localName, true);

        // don't process skip tokens
        if (kind == ParserConstants.SKIP_ELEMENT)
        {
            return;
        }

        int numAttributes = attributes.getLength();
        Node n = createNode(kind, uri, localName, attributes);
        n.addPrefixMapping(uri, prefixMappings.get(uri));
        assignTokenPosition(n);

        Method lineNumMethod = null;
        // C: if n.beginLine == n.endLine, then we don't really need to do
        // this...
        if (numAttributes > 0 && CustomAttributeClass != null && attributes.getClass().isAssignableFrom(CustomAttributeClass))
        {
            try
            {
                lineNumMethod = attributes.getClass().getMethod("getLineNumber", new Class[] {Integer.TYPE});
            }
            catch (NoSuchMethodException ex)
            {
                // do nothing... really...
            }
        }

        for (int i = 0; i < numAttributes; i++)
        {
            int line = n.beginLine;

            try
            {
                if (lineNumMethod != null)
                {
                    line = Integer.parseInt(lineNumMethod.invoke(attributes, new Object[] {new Integer(i)}).toString());
                }
            }
            catch (IllegalAccessException ex)
            {
            }
            catch (InvocationTargetException ex)
            {
            }

            String attrUri = attributes.getURI(i);
            n.addAttribute(attrUri, attributes.getLocalName(i), attributes.getValue(i), line);
            n.addPrefixMapping(attrUri, prefixMappings.get(attrUri));
        }

        n.kind = kind;
        n.image = "<" + qName + ">";

        // if there was a comment, lets stick it inside the node.
        if (cdataHandler.comment != null)
        {
            n.comment = cdataHandler.comment;

            // clear up the comment.
            cdataHandler.comment = null;
        }

        // SDK-22601: a workaround for the custom whitespace preservation
        // requirements for spark text components (as MXML does not preserve
        // whitespace outside of a CDATA block). TODO: replace this with a
        // generic mechanism.
        if (SPARK_NAMESPACE.equals(uri))
        {
            if (SPARK_TEXT_TAGS.contains(localName))
            {
                textDepth++;
                n.processChildrenIndividually = true;
                n.preserveWhitespace = true;

                // SDK-24699: a workaround for the case where text was
                // explicitly set via an attribute but whitespace exists as
                // character data in the tag (which would otherwise be assigned
                // to the default property and clobber the text attribute).
                if (attributes.getValue("", SPARK_TEXT_ATTRIBUTE) != null
                        || attributes.getValue(uri, SPARK_TEXT_ATTRIBUTE) != null)
                {
                    n.preserveWhitespace = false;
                }
            }
            else if (textDepth > 0)
            {
                // We must also preserve whitespace under explicit text content
                // properties.
                if (SPARK_TEXT_CONTENT_PROPERTIES.contains(localName))
                {
                    n.processChildrenIndividually = true;
                    n.preserveWhitespace = true;
                }

                // If a spark text tag has a child tag that is not part of
                // text mark-up, we won't preserve whitespace for character
                // data directly attached to this node. This is to avoid bug
                // SDK-23972.
                if (!elementStack.empty())
                {
                    Node parent = (Node) elementStack.peek();
                    parent.preserveWhitespace = false;
                }       
            }
        }

        saxEvents.add(n);
        elementStack.push(n);

        beginLine = locator.getLineNumber();
        beginColumn = locator.getColumnNumber();
    }

    public void endElement(String uri, String localName, String qName)
            throws SAXException
    {
        Token n = new Token();

        n.kind = findElementType(uri, localName, false);

        // don't process skip tokens
        if (n.kind == ParserConstants.SKIP_ELEMENT)
        {
            return;
        }

        assignTokenPosition(n);
        n.image = "</" + qName + ">";

        saxEvents.add(n);

        // As part of a workaround for SDK-22601 we stop preserving whitespace
        // when we leave the scope of a text tag.
        if (textDepth > 0 && SPARK_NAMESPACE.equals(uri) && SPARK_TEXT_TAGS.contains(localName))
        {
            textDepth--;
        }

        if (!elementStack.empty())
        {
            Node prevNode = (Node) elementStack.peek();   
            if (prevNode.getNamespace().equals(uri) && prevNode.getLocalPart().equals(localName) )
                elementStack.pop();
        }

        kind = n.kind;

        beginLine = locator.getLineNumber();
        beginColumn = locator.getColumnNumber();
    }

    public void characters(char ch[], int start, int length)
            throws SAXException
    {
        String image = new String(ch, start, length);
        Object obj = saxEvents.get(saxEvents.size() - 1);
        boolean wasInCDATA = (obj instanceof CDATANode && ((CDATANode)obj).inCDATA), skip = false;
        CDATANode cdata = (obj instanceof CDATANode) ? (CDATANode)obj : null;

        if (wasInCDATA && cdataHandler.inCDATA)
        {
            // do not null out cdata...
        }
        else if (wasInCDATA && !cdataHandler.inCDATA)
        {
            if (image.trim().length() > 0)
            {
                cdata = null;
            }
            else
            {
                skip = true;
            }
        }
        else if (!wasInCDATA && cdataHandler.inCDATA)
        {
            cdata = null;
        }
        else
        // if (!wasInCDATA && !cdataHandler.inCDATA)
        {
            // do not null out cdata...
            if (kind != ParserConstants.CDATA
                    && image.trim().length() == 0)
            {
                // As part of a workaround for SDK-22601 we skip whitespace if
                // the parent tag does not preserve it.
                if (textDepth > 0)
                {
                    // Check to see if the parent node will preserve whitespace
                    if (!elementStack.empty())
                    {
                        Node parent = (Node) elementStack.peek();
                        if (!parent.preserveWhitespace)
                            skip = true;
                    }
                }
                else
                {
                    skip = true;
                }
            }
        }

        if (!skip && cdata == null)
        {
            cdata = new CDATANode();
            cdata.kind = ParserConstants.CDATA;
            assignTokenPosition(cdata);
            cdata.image = image;
            cdata.inCDATA = cdataHandler.inCDATA;
            saxEvents.add(cdata);
        }
        else if (!skip)
        {
            cdata.image += image;
            // C: only reassign the node's end position.
            cdata.endLine = locator.getLineNumber();
            cdata.endColumn = locator.getColumnNumber();
        }

        if (!skip)
        {
            kind = ParserConstants.CDATA;
        }

        beginLine = locator.getLineNumber();
        beginColumn = locator.getColumnNumber();
    }

    public void warning(SAXParseException e) throws SAXException
    {
        int line = (locator != null) ? locator.getLineNumber() : 1;
        int col = (locator != null) ? locator.getColumnNumber() : 1;

        ScannerError err = new ScannerError(line, col, e.getMessage());
        saxEvents.add(err);
    }

    public void error(SAXParseException e) throws SAXException
    {
        int line = (locator != null) ? locator.getLineNumber() : 1;
        int col = (locator != null) ? locator.getColumnNumber() : 1;

        ScannerError err = new ScannerError(line, col, e.getMessage());
        saxEvents.add(err);
    }

    public void fatalError(SAXParseException e) throws SAXException
    {
        int line = (locator != null) ? locator.getLineNumber() : 1;
        int col = (locator != null) ? locator.getColumnNumber() : 1;

        ScannerError err = new flex2.compiler.mxml.dom.ScannerError(line, col, e.getMessage());
        saxEvents.add(err);
        throw e;
    }

    private class CDATAHandler implements LexicalHandler
    {
        CDATAHandler(boolean processComments)
        {
            this.processComments = processComments;
        }

        boolean inCDATA = false;

        // field to store comment.
        String comment = null;

        // field to check if we care about comments.
        boolean processComments = false;

        public void startCDATA() throws SAXException
        {
            inCDATA = true;
        }

        public void endCDATA() throws SAXException
        {
            inCDATA = false;
        }

        public void startDTD(String s, String s1, String s2)
                throws SAXException
        {
        }

        public void endDTD() throws SAXException
        {
        }

        public void startEntity(String s) throws SAXException
        {
        }

        public void endEntity(String s) throws SAXException
        {
        }

        public void comment(char[] chars, int i, int i1) throws SAXException
        {
            // only process comments if entry is from asdoc.
            if (!processComments)
            {
                return;
            }

            String temp = new String(chars, i, i1);
            // only process comments which are nested in <!---, the ones with <!-- are regular comments.
            // <!--- 3 dashes are *required*.
            if (temp != null && temp.startsWith("-"))
            {
                temp = temp.substring(1); // ignore the starting dash (-)

                temp = temp.trim();

                // removing the prefix /** and suffix */ - this helps if we user
                // -keep-generated-actionscript, other the generated actionscript
                // breaks the comment then looks like /** /** blah */ */
                if (temp.startsWith("/**"))
                {
                    temp = temp.substring(3);
                }

                if (temp.endsWith("*/"))
                {
                    temp = temp.substring(0, temp.length() - 2);
                }
               
                // replace all - at the start of a line.
                // This should help to support mxml comments like
                //  <!---
                //    - blah
                //    - @private 
                //    -->
                Pattern pattern = Pattern.compile("^\\s*\\-", Pattern.MULTILINE);
                Matcher matcher = pattern.matcher(temp);
                comment = matcher.replaceAll("");
            }
        }
    }

    private static int getLanguageVersion(String uri)
    {
        if (MXML_2009_NAMESPACE.equals(uri) || FXG_2008_NAMESPACE.equals(uri))
        {
            return 4;
        }
        else if (MXML_2006_NAMESPACE.equals(uri))
        {
            return 3;
        }

        return -1;
    }

    // error messages

    public static class ConflictingLanguageVersion extends
            CompilerMessage.CompilerError
    {
        private static final long serialVersionUID = -9106905839748620966L;
        public final String ns1;
        public final String ns2;

        public ConflictingLanguageVersion(String ns1, String ns2)
        {
            this.ns1 = ns1;
            this.ns2 = ns2;
        }
    }
   
    public static class UnexpectedProcessingInstruction extends CompilerMessage.CompilerWarning
    {
        private static final long serialVersionUID = -6576251697820779447L;

        public UnexpectedProcessingInstruction(String reason)
        {
            super();
            this.reason = reason;
        }

        public final String reason;
    }
   
    public static class XMLTagAttributeLineNumber extends
            CompilerMessage.CompilerWarning
    {
        private static final long serialVersionUID = -461995005628630535L;

        public XMLTagAttributeLineNumber()
        {
            super();
            noPath();
        }
    }

    public static class UnexpectedEndOfTokenStream extends
            CompilerMessage.CompilerError
    {
        private static final long serialVersionUID = 4354585678545293412L;

        public UnexpectedEndOfTokenStream(String token)
        {
            super();
            this.token = token;
        }

        public final String token;
    }

    public static class UnexpectedEndOfSAXStream extends
            CompilerMessage.CompilerError
    {
        private static final long serialVersionUID = -3989054372427510063L;

        public UnexpectedEndOfSAXStream(String reason)
        {
            super();
            this.reason = reason;
        }

        public final String reason;
    }
}
TOP

Related Classes of flex2.compiler.mxml.dom.MxmlScanner$XMLTagAttributeLineNumber

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.