Package com.adobe.internal.fxg.sax

Source Code of com.adobe.internal.fxg.sax.FXGSAXScanner

/*
*
*  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 com.adobe.internal.fxg.sax;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.adobe.fxg.util.FXGLog;
import com.adobe.fxg.util.FXGLogger;
import com.adobe.fxg.FXGException;
import com.adobe.fxg.FXGConstants;
import com.adobe.fxg.dom.FXGNode;

import com.adobe.internal.fxg.dom.CDATANode;
import com.adobe.internal.fxg.dom.GraphicNode;
import com.adobe.internal.fxg.dom.DefinitionNode;
import com.adobe.internal.fxg.dom.DelegateNode;
import com.adobe.internal.fxg.dom.PreserveWhiteSpaceNode;

import static com.adobe.fxg.FXGConstants.*;

/**
* This SAX2 based scanner converts an FXG document (an XML based description of
* a graphical asset) to a simple object graph to serve as an intermediate
* representation. The document must be in the FXG 1.0 namespace and the root
* element must be a <Graphic> tag.
*
* @author Peter Farland
* @author Sujata Das
*/
public class FXGSAXScanner extends DefaultHandler
{
    // Namespaces
    public static final String APACHE_FLEX_NAMESPACE = "http://ns.apache.org/flex/2012";
   
    private static boolean REJECT_MAJOR_VERSION_MISMATCH = false;
   
    // A special case needed to short circuit GroupNode creation inside a
    // Definition as such Groups are not the same as those in the graphics
    // tree.
    private static final String FXG_GROUP_DEFINITION_ELEMENT = "[GroupDefinition]";
       
    private String profile;
    private GraphicNode root;
    private Stack<FXGNode> stack;
    private int skippedElementCount;
    private boolean seenPrivateElement = false;
    private boolean inMaskAfterPrivateElement = false;
    private Locator locator;
    private int startLine = 0;
    private int startColumn = 0;
    private String documentName = null;
    private String unknownElement = null;
   
    // FXG version handler to handle different fxg versions
    // depending on input file version at runtime.
    private FXGVersionHandler versionHandler = null;
   
    /**
     * Construct a new FXGSAXScanner
     */
    public FXGSAXScanner(String profile)
    {
        super();
        this.profile = profile;
        if (profile.equals(FXG_PROFILE_MOBILE))
            versionHandler = FXGVersionHandlerRegistry.getDefaultMobileHandler();
        else
            versionHandler = FXGVersionHandlerRegistry.getDefaultHandler();
        if (versionHandler == null)
            throw new FXGException("FXGVersionHandlerNotRegistered", FXGVersionHandlerRegistry.defaultVersion.asDouble());
    }

    /**
     * Provides access to the root FXGNode of the FXG document AFTER parsing.
     *
     * @return the root FXGNode of the DOM.
     */
    public FXGNode getRootNode()
    {
        return root;
    }

    //--------------------------------------------------------------------------
    //
    // SAX DefaultHandler Implementation
    //
    //--------------------------------------------------------------------------

    /**
     * {@inheritDoc}
     */
    public void setDocumentLocator(Locator locator)
    {
        this.locator = locator;
    }
   
    /**
     * Set document name used for logging.
     *
     * @return the document name
     */
    public String getDocumentName()
    {
        return documentName;
    }

    /**
     * Get document name used for logging.
     *
     * @param documentName the document name
     */
    public void setDocumentName(String documentName)
    {
        this.documentName = documentName;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void startDocument() throws SAXException
    {
        stack = new Stack<FXGNode>();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException
    {
        // First check if we're currently skipping elements
        if (isSkippedElement(uri, localName, true))
            skippedElementCount++;
        if (inSkippedElement())
            return;
       
        // Check if we're currently skipping unknown elements
        if (unknownElement != null)
          return;

        // Record starting position
        startLine = locator.getLineNumber();
        startColumn = locator.getColumnNumber();

        // Check the current parent
        FXGNode parent = null;
        if (stack.size() > 0)
            parent = stack.peek();

        // Switch to special GroupDefinitionNode for Definition child
        if (isFXGNamespace(uri))
        {
            if (parent instanceof DefinitionNode && FXG_GROUP_ELEMENT.equals(localName))
                localName = FXG_GROUP_DEFINITION_ELEMENT;
        }

        // Create a node for this element
        FXGNode node = createNode(uri, localName);
       
        if (node == null)
        {
            if (root != null)
            {
                if (root.isVersionGreaterThanCompiler())
                {
                    // Warning: Minor version of this FXG file is greater than minor
                    // version supported by this compiler. Log a warning for an
                    // unknown element.
                    FXGLog.getLogger().log(FXGLogger.WARN, "UnknownElement", null, documentName, startLine, startColumn, localName, versionHandler.getVersion().asString());
                    unknownElement = localName;
                    return;
                }else
                {
                    throw new FXGException(startLine, startColumn, "UnknownElementInVersion", root.getFileVersion().asString(), localName);                   
                }
            }
            else
            {
                throw new FXGException(startLine, startColumn, "InvalidFXGRootNode");
            }
        }

        // Provide access to the root document node used for querying version
        // for non-root elements
        if (root != null)
        {
            node.setDocumentNode(root);
        }
       
        // Set node name if it is a delegate node. This allows proper error
        // message to be reported.
        if (node instanceof DelegateNode)
        {
            DelegateNode propertyNode = (DelegateNode)node;
            propertyNode.setName(localName);
        }
       
        // Set attributes on the current node
        for (int i = 0; i < attributes.getLength(); i++)
        {
            String attributeURI = attributes.getURI(i);
            if (attributeURI == null || attributeURI == "" ||
                isFXGNamespace(attributeURI) ||
                isApacheFlexNamespace(attributeURI))
            {
                String attributeName = attributes.getLocalName(i);
                String attributeValue = attributes.getValue(i);
                node.setAttribute(attributeName, attributeValue);
            }
        }

        // Associate child with parent node (and handle any special
        // relationships)
        if (parent != null)
        {
            if (node instanceof DelegateNode)
            {
                DelegateNode propertyNode = (DelegateNode)node;
                propertyNode.setDelegate(parent);
            }
            else
            {
                parent.addChild(node);
            }
        }
        else if (node instanceof GraphicNode)
        {
            root = (GraphicNode)node;
            // Provide access to the root document node
            node.setDocumentNode(root);
            if (root.getVersion() == null)
            {
                // Exception: <Graphic> doesn't have the required attribute
                // "version".
                throw new FXGException(startLine, startColumn, "MissingVersionAttribute");
            }
            else
            {
                if (!isMajorVersionMatch(root))
                {
                    FXGVersionHandler newVHandler = FXGVersionHandlerRegistry.getVersionHandler(root.getVersion());
                  
                    if (newVHandler == null)
                    {
                        if  (REJECT_MAJOR_VERSION_MISMATCH)
                        {
                            // Exception:Major version of this FXG file is greater than
                            // major version supported by this compiler. Cannot process
                            // the file.
                            throw new FXGException(startLine, startColumn, "InvalidFXGVersion", root.getVersion().asString());
                        }
                        else
                        {
                            // Warning: Major version of this FXG file is greater than
                            // major version supported by this compiler.
                            FXGLog.getLogger().log(FXGLogger.WARN, "MajorVersionMismatch", null, getDocumentName(), startLine, startColumn);

                            //use the latest version handler
                            versionHandler = FXGVersionHandlerRegistry.getLatestVersionHandler();
                            if (versionHandler == null)
                            {  
                                throw new FXGException("FXGVersionHandlerNotRegistered", root.getVersion().asString());                             
                            }                          
                        }
                    }
                    else
                    {
                        versionHandler = newVHandler;                       
                    }
                }
            }
            // Provide reference to the handler for querying version of the
            // current document processed.
            root.setDocumentName(documentName);
            root.setVersionGreaterThanCompiler(root.getVersion().greaterThan(versionHandler.getVersion()));
            root.setReservedNodes(versionHandler.getElementNodes(uri));
            root.setCompilerVersion(versionHandler.getVersion());
            root.setProfile(profile);
        }
        else
        {
            // Exception:<Graphic> must be the root node of an FXG document.
            throw new FXGException(startLine, startColumn, "InvalidFXGRootNode");
        }

        stack.push(node);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException
    {
        if (stack != null && stack.size() > 0 && !inSkippedElement() && (unknownElement == null))
        {
            FXGNode node = stack.peek();
            String content = new String(ch, start, length);

            if (!(node instanceof PreserveWhiteSpaceNode))
            {
                content = content.trim();
            }
           
            if (content.length() > 0)
            {
                CDATANode cdata = new CDATANode();
                cdata.content = content;
                assignNodeLocation(cdata);
                node.addChild(cdata);
            }
        }

        // Reset starting position
        startLine = locator.getLineNumber();
        startColumn = locator.getColumnNumber();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void endElement(String uri, String localName, String name)
            throws SAXException
    {
        if (isSkippedElement(uri, localName, false))
        {
            skippedElementCount--;
        }
        else if (unknownElement != null)
        {
            if (unknownElement.equals(localName))
            {
                unknownElement = null;
            }
        }
        else if (!inSkippedElement())
        {
            stack.pop();
        }
       
        // Reset starting position
        startLine = locator.getLineNumber();
        startColumn = locator.getColumnNumber();
    }


    //--------------------------------------------------------------------------
    //
    // Other Methods
    //
    //--------------------------------------------------------------------------

    /**
     * @return the last processed line number
     */
    public int getStartLine()
    {
        return startLine;
    }

    /**
     * @return the last processed column number
     */
    public int getStartColumn()
    {
        return startColumn;
    }

    /**
     * @param uri - the namespace URI to check
     * @return whether the given namespace URI is considered an FXG namespace.
     */
    protected boolean isFXGNamespace(String uri)
    {
        return FXG_NAMESPACE.equals(uri);
    }

    /**
     * @param uri - the namespace URI to check
     * @return whether the given namespace URI is considered an Apache Flex namespace.
     */
    protected boolean isApacheFlexNamespace(String uri)
    {
        return APACHE_FLEX_NAMESPACE.equals(uri);
    }

    /**
     * Specifies that a particular element should be skipped while scanning for
     * tokens in an FXG document. All of the element's attributes and child
     * nodes will be skipped too.
     *
     * @param version - the version of the FXG element
     * @param uri - the namespace URI of the element to skip
     * @param localName - the name of the element to skip
     */
    protected void skipElement(double version, String uri,  String localName)
    {
        if (localName == null)
            return;

        FXGVersionHandler versionHandler = FXGVersionHandlerRegistry.getVersionHandler(version);
        if (versionHandler != null)
        {
            HashSet<String>skippedElements = new HashSet<String>(1);
            skippedElements.add(localName);
            versionHandler.registerSkippedElements(uri, skippedElements);           
        }
        else
        {
            throw new FXGException("FXGVersionHandlerNotRegistered", version);
        }

    }
   

    /**
     * Determines whether an element should be skipped.
     *
     * @param uri - the namespace URI of the element
     * @param localName - the name of the element
     * @return true if the element has been marked as skipped, otherwise false.
     */
    protected boolean isSkippedElement(String uri, String localName, boolean startElement)
    {
        Set<String> skippedElements = versionHandler.getSkippedElements(uri);
        if (skippedElements != null)
        {
            if (skippedElements.contains(FXGConstants.FXG_PRIVATE_ELEMENT))
            {
                validatePrivateElement(localName, startElement);
            }
            if (skippedElements.contains(localName))
            {   
                return true;
            }
        }

        return false;
    }

   
    /**
     * Attempts to construct an instance of FXGNode for the given element.
     *
     * @param uri - the namespace URI of the element
     * @param localName - the name of the element
     * @return FXGNode instance if
     */
    protected FXGNode createNode(String uri, String localName)
    {
        FXGNode node = null;

        try
        {
            Map<String, Class<? extends FXGNode>> elementNodes = getElementNodes(uri);
            if (elementNodes != null)
            {
                Class<? extends FXGNode> nodeClass = elementNodes.get(localName);
                if (nodeClass != null)
                {
                    node = (FXGNode)nodeClass.newInstance();
                }
                else if (root != null)
                {
                    node = root.getDefinitionInstance(localName);
                }
            }
        }
        catch (Throwable t)
        {
            throw new FXGException(startLine, startColumn, "ErrorScanningFXG", t);
        }

        if (node != null)
        {
            assignNodeLocation(node);
        }

        return node;
    }

    /**
     * @return if currently in a skipped element.
     */
    private boolean inSkippedElement()
    {
        return skippedElementCount > 0;
    }
   
    /**
     * Registers a custom FXGNode for a particular type of element encountered
     * while scanning an FXG document.
     *
     * @param version - the version of the FXG element
     * @param uri - the namespace URI of the FXG element
     * @param localName - the local name of the FXG element
     * @param nodeClass - Class of an FXGNode implementation that will represent
     * an element in the DOM and process its attributes and child nodes during
     * parsing.
     */
    protected void registerElementNode(double version, String uri, String localName, Class<? extends FXGNode> nodeClass)
    {
        FXGVersionHandler vHandler = FXGVersionHandlerRegistry.getVersionHandler(version);
        if (vHandler != null)
        {
            HashMap<String, Class<? extends FXGNode>> elementNodes = new HashMap<String, Class<? extends FXGNode>>(4);
            elementNodes.put(localName, nodeClass);
            vHandler.registerElementNodes(uri, elementNodes);
       
        else
        {
            throw new FXGException("FXGVersionHandlerNotRegistered", version);
        }
    }


    /**
     * Record the start and end line and column information for this node.
     * @param node - the current node
     */
    private void assignNodeLocation(FXGNode node)
    {
        if (node != null)
        {
            node.setStartLine(startLine);
            node.setStartColumn(startColumn);
            node.setEndLine(locator.getLineNumber());
            node.setEndColumn(locator.getColumnNumber());
        }
    }

    /**
     * @param uri - the namespace URI of the registered FXG elements.
     * @return a Map of the FXGNode Classes registered for elements in the
     * given namespace URI.
     */
    private Map<String, Class<? extends FXGNode>> getElementNodes(String uri)
    {
        return versionHandler.getElementNodes(uri);
    }

    /**
     * validates restrictions on PRIVATE element
     * @param localName
     */
    private void validatePrivateElement(String localName, boolean startElement)
    {
        if (!startElement)
        {
            if (inMaskAfterPrivateElement && localName.equals(FXGConstants.FXG_MASK_ELEMENT))
                inMaskAfterPrivateElement = false;
            return;
        }

        if (localName.equals(FXGConstants.FXG_PRIVATE_ELEMENT))
        {
            if (seenPrivateElement)
            {
                throw new FXGException("PrivateElementMultipleOccurrences", startLine, startColumn);
            }
            else
            {
                if ((!inSkippedElement()) && stack.size() == 1)
                    seenPrivateElement = true;
                else
                    throw new FXGException("PrivateElementNotChildOfGraphic", startLine, startColumn);
            }
        }
        else
        {
            if (seenPrivateElement && (!inSkippedElement()))
            {
                if ((!inMaskAfterPrivateElement) && (localName.equals(FXGConstants.FXG_MASK_ELEMENT)))
                {
                    inMaskAfterPrivateElement = true;
                }
                else
                {
                    if (!inMaskAfterPrivateElement)
                        throw new FXGException("PrivateElementNotLast", startLine, startColumn);
                }
            }
        }
    }
   
    /**
     * @return - true if major version of the FXG file matches the compiler's
     * major version. false otherwise.
     */
    private boolean isMajorVersionMatch(GraphicNode root)
    {
        long majorVersion = root.getVersion().getMajorVersion();
        long compilerMajorVersion = versionHandler.getVersion().getMajorVersion();
        if (majorVersion == compilerMajorVersion)
            return true;
        else
            return false;
    }
   
}
TOP

Related Classes of com.adobe.internal.fxg.sax.FXGSAXScanner

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.