themeinstaller/source/src/com/nokia/tools/themeinstaller/odtconverter/DOMExternalizer.java
branchRCL_3
changeset 18 04b7640f6fb5
parent 0 05da4621cfb2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/themeinstaller/source/src/com/nokia/tools/themeinstaller/odtconverter/DOMExternalizer.java	Wed Sep 01 12:32:13 2010 +0100
@@ -0,0 +1,718 @@
+/*
+* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies). 
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  Externalizes a DOM Document to a byte array.
+ *
+*/
+
+
+package com.nokia.tools.themeinstaller.odtconverter;
+
+import java.awt.Color;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.nokia.tools.themeinstaller.cssparser.CSSDOMProcessor;
+import com.nokia.tools.themeinstaller.cssparser.CSSHandler;
+import com.nokia.tools.themeinstaller.cssparser.PseudoClassResolver;
+
+/**
+ * Externalizes DOM Document to a byte array.
+ */
+public class DOMExternalizer
+    {
+
+    // CONSTANTS
+    // Text elements according to CXnDomVisitor::KXnElementVisitorTable and
+    // CXnODTParser
+    private static final String TEXT_ELEMENT = "text";
+    private static final String DESC_ELEMENT = "desc";
+    private static final String MARQUEE_ELEMENT = "marquee";
+    private static final String OBJECT_ELEMENT = "object";
+    private static final String NEWSTICKER_ELEMENT = "newsticker";
+
+    // Attribute name if node is refnode
+    private static final String REF_NODE = "ref";
+
+    // List types
+    private static final int UNKNOWN = -1;
+    private static final int NODE = 0;
+    private static final int ATTRIBUTE = 1;
+    private static final int PROPERTY = 2;
+
+    // The Constant COLOR_TABLE_SIZE
+    private static final int COLOR_TABLE_SIZE = 3;
+
+    // Pseudo class resolver
+    private PseudoClassResolver iPseudoResolver;
+
+    // The value type resolver
+    private ValueTypeResolver iValueTypeResolver;
+
+    // String pool
+    private StringPool iStringPool;
+
+    // DOM Document
+    private Document iDocument;
+
+    // Byte output stream for delivering the externalized data
+    private ByteArrayOutputStream iBaos;
+
+    // Data output stream for writing to the baos
+    private ODTDataOutputStream iODTDos;
+
+    // Node id and counter
+    private int iCurrentNodeId = 0;
+
+    /** CSSDOMProsessor for checking property's inheritance value. */
+    private CSSDOMProcessor iCSSDOMProsessor;
+
+    /**
+     * Constructor.
+     * @param aDocument DOM Document to externalize
+     */
+    public DOMExternalizer( Document aDocument )
+        {
+        iPseudoResolver = new PseudoClassResolver();
+        iValueTypeResolver = new ValueTypeResolver();
+        iCSSDOMProsessor = new CSSDOMProcessor();
+        iStringPool = new StringPool();
+        iDocument = aDocument;
+        iBaos = new ByteArrayOutputStream();
+        iODTDos = new ODTDataOutputStream( iBaos );
+        }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#finalize()
+     */
+    protected void finalize() throws Throwable
+        {
+        iODTDos.close();
+        iBaos.close();
+        }
+
+    /**
+     * Externalizes the DOM document and string pool to a byte array.
+     * @return Byte array containing the document
+     * @throws IOException if writing to a stream fails
+     * @throws ODTException
+     */
+    public byte[] getByteArray() throws IOException, ODTException
+        {
+        ByteArrayOutputStream result = new ByteArrayOutputStream();
+        byte[] resultArray = null;
+
+        try
+            {
+            // Externalize the DOM
+            doExternalize();
+
+            // Externalize the string pool
+            result.write( iStringPool.toByteArray() );
+
+            // Write the dom contents to the result baos
+            result.write( iBaos.toByteArray() );
+
+            resultArray = result.toByteArray();
+            }
+        finally
+            {
+            if( result != null )
+                {
+                result.close();
+                }
+            }
+
+        return resultArray;
+        }
+
+    /**
+     * Do the externalization process.
+     * @throws IOException if data output stream can not be written
+     * @throws ODTException if there is an error with property value externalization
+     */
+    private void doExternalize() throws IOException, ODTException
+        {
+        Node rootElement = iDocument.getDocumentElement();
+
+        if( rootElement != null )
+            {
+            // Root node exists
+            iODTDos.writeBoolean( true );
+            externalizeNode( ( Node )rootElement, true );
+            }
+        else
+            {
+            // No root node
+            iODTDos.writeBoolean( false );
+            }
+        }
+
+    /**
+     * Externalizes a node in the DOM tree.
+     * @param aNode Node to externalize
+     * @param aRootNode true, if this is the root node of the tree
+     * @throws IOException if data output stream can not be written
+     * @throws ODTException if there is an error with property value externalization
+     */
+    private void externalizeNode( Node aNode, boolean aRootNode )
+        throws IOException, ODTException
+        {
+        // Write name
+        String name = aNode.getNodeName();
+        int nameRef = iStringPool.addString( name );
+        iODTDos.writeInt16( nameRef );
+
+        // Write name space
+        String ns = aNode.getNamespaceURI();
+        int nsRef = iStringPool.addString( ns );
+        iODTDos.writeInt16( nsRef );
+
+        // Check and write refnode boolean
+        iODTDos.writeBoolean( checkRefNode( aNode ) );
+
+        boolean textNodeFound = false;
+
+        // Write parsed character data
+        StringBuffer pcData = new StringBuffer();
+        if( TEXT_ELEMENT.equals( name ) ||
+            DESC_ELEMENT.equals( name ) ||
+            MARQUEE_ELEMENT.equals( name ) ||
+            OBJECT_ELEMENT.equals( name ) ||
+            NEWSTICKER_ELEMENT.equals( name ) )
+            {
+            NodeList list = aNode.getChildNodes();
+
+            for( int i = 0; i < list.getLength() ; i++ )
+                {
+                Node nod = list.item( i );
+                if( nod.getNodeType() == Node.TEXT_NODE )
+                    {
+                    textNodeFound = true;
+                    String textNodeValue = nod.getNodeValue();
+
+                    pcData.append( textNodeValue );
+                    }
+                }
+            }
+
+        // Write data length and the data
+        iODTDos.writeBoolean( textNodeFound );
+        if( textNodeFound )
+            {
+            // Write text length
+            iODTDos.writeInt16( pcData.length() );
+
+            // Write text node value
+            iODTDos.writeString8( pcData.toString() );
+            }
+        // Update the counter and write node id
+        iODTDos.writeInt32( iCurrentNodeId++ );
+
+        // Externalize the child list
+        externalizeNodeList( aNode );
+
+        // Externalize the attribute list
+        externalizeAttributeList( aNode );
+
+        // Externalize the property list
+        externalizePropertyList( aNode );
+        }
+
+    /**
+     * Checks if node is refnode.
+     * Node is refnode if some of node's attributes name is "ref".
+     * @param aNode Node containing the attributes
+     * @return boolean true if node is refnode, otherwise false
+     */
+    private boolean checkRefNode( Node aNode )
+        {
+        boolean refNode = false;
+        NamedNodeMap list = aNode.getAttributes();
+        for ( int i = 0; i < list.getLength(); i++ )
+            {
+            Node node = list.item( i );
+            if ( nodeType( node ) == ATTRIBUTE )
+                {
+                if( node.getNodeName().equals( REF_NODE ) )
+                    {
+                    refNode = true;
+                    }
+                }
+            }
+        return refNode;
+        }
+
+    /**
+     * Externalizes attributes of a node.
+     * @param aNode Node containing the attributes
+     * @throws IOException if writing to a stream fails
+     * @throws ODTException
+     */
+    private void externalizeAttributeNode( Node aNode )
+        throws IOException, ODTException
+        {
+        // Write name
+        String name = aNode.getNodeName();
+        int nameRef = iStringPool.addString( name );
+        iODTDos.writeInt16( nameRef );
+
+        // Write value
+        String value = aNode.getNodeValue();
+        int valueRef = iStringPool.addString( value );
+        iODTDos.writeInt16( valueRef );
+        }
+
+    /**
+     * Externalizes a property node.
+     *
+     * @param aNode Node containing the properties
+     * @param aParentNode Node's parent node
+     *
+     * @throws IOException if data output stream can not be written
+     * @throws ODTException if there is an error with property
+     * value externalization
+     */
+    private void externalizePropertyNode( Node aParentNode, Node aNode )
+        throws IOException, ODTException
+        {
+        // Get property node attributes: property name, values and pseudo class
+        NamedNodeMap list = aNode.getAttributes();
+
+        // Property value list
+        boolean isInherited = externalizePropertyValueList( aParentNode, list );
+
+        iODTDos.writeBoolean( isInherited );
+
+        // Resolve pseudo class
+        int pseudoClass = PseudoClassResolver.NONE;
+
+        int count = list.getLength();
+        for( int i = 0; i < count; i++ )
+            {
+            // Browse through all attributes of the style property element and
+            // seek for a pseudo class
+            Node item = list.item( i );
+            if( item.getNodeName().equals( CSSDOMProcessor.STRING_PSEUDOCLASS ) )
+                {
+                pseudoClass = iPseudoResolver.getKey( item.getNodeValue() );
+                if( pseudoClass == UNKNOWN )
+                    {
+                    throw new ODTException( "Error externalizing ODT/styles: " +
+                    		"unknown pseudo class" );
+                    }
+                break;
+                }
+            }
+
+        // Pseudo class -> int8
+        iODTDos.writeByte( pseudoClass );
+        }
+
+    /**
+     * Externalize a list of nodes.
+     * @param aParentNode Parent node
+     * @throws IOException if data output stream can not be written
+     * @throws ODTException if there is an error with property value externalization
+     */
+    private void externalizeNodeList( Node aParentNode ) throws IOException, ODTException
+        {
+        // Write list type
+        iODTDos.writeByte( NODE );
+
+        NodeList list = aParentNode.getChildNodes();
+
+        // Count element nodes
+        int count = countNodes( list );
+
+        // Write element node count
+        iODTDos.writeInt32( count );
+
+        for ( int i = 0; i < list.getLength(); i++ )
+            {
+            Node node = list.item( i );
+
+            if ( nodeType( node ) == NODE )
+                {
+                externalizeNode( node, false );
+                }
+            }
+        }
+
+    /**
+     * Externalize a list of attributes.
+     * @param aParentNode Node containing the attributes.
+     * @throws IOException if writing to a stream fails
+     * @throws ODTException
+     */
+    private void externalizeAttributeList( Node aParentNode )
+            throws IOException, ODTException
+        {
+        // Write list type
+        iODTDos.writeByte( ATTRIBUTE );
+
+        NamedNodeMap list = aParentNode.getAttributes();
+
+        // Count element nodes
+        int count = countAttributes( list );
+
+        // Write element node count
+        iODTDos.writeInt32( count );
+
+        for ( int i = 0; i < list.getLength(); i++ )
+            {
+            Node node = list.item( i );
+
+            if ( nodeType( node ) == ATTRIBUTE )
+                {
+                externalizeAttributeNode( node );
+                }
+            }
+        }
+
+    /**
+     * Externalize a list of properties.
+     * @param aParentNode Node containing the properties
+     * @throws IOException if data output stream can not be written
+     * @throws ODTException if there is an error with property value externalization
+     */
+    private void externalizePropertyList( Node aParentNode ) throws IOException, ODTException
+        {
+        // Write list type
+        iODTDos.writeByte( PROPERTY );
+
+        NodeList list = aParentNode.getChildNodes();
+
+        // Count element nodes
+        int count = countProperties( list );
+
+        // Write element node count
+        iODTDos.writeInt32( count );
+
+        for ( int i = 0; i < list.getLength(); i++ )
+            {
+            Node node = list.item( i );
+
+            if ( nodeType( node ) == PROPERTY )
+                {
+                externalizePropertyNode( aParentNode, node );
+                }
+            }
+        }
+
+    /**
+     * Parses a Color from string.
+     *
+     * @param aColor Color values in String made by CSSHandler
+     *
+     * @return The color
+     *
+     * @throws ODTException the ODT exception
+     */
+    private Color parseColorFromString( String aColor ) throws ODTException
+        {
+        String sx[] = aColor.split( CSSHandler.COMMA );
+        int ix[] = new int[ sx.length ];
+
+        // LexicalUnit.SAC_RGBCOLOR knows color values with format
+        // rgb(0, 0, 0) and #000. That is, with three parameters
+        if ( sx.length != COLOR_TABLE_SIZE )
+            {
+            throw new ODTException( "Error in DOM/style data externalization: "
+                    + "RGB Values: parameter amount" );
+            }
+
+        for ( int i = 0; i < sx.length; i++ )
+            {
+            if ( sx[ i ].contains( CSSHandler.SEPARATOR ) )
+                {
+                // Check if single color's value is integer
+                Short valueType = iValueTypeResolver.getValue( Short
+                        .valueOf( sx[ i ].substring( 0, sx[ i ]
+                                .indexOf( CSSHandler.SEPARATOR ) ) ) );
+
+                if ( valueType.intValue() != ValueTypeResolver.E_NUMBER )
+                    {
+                    throw new ODTException(
+                            "Error in DOM/style data externalization: "
+                                    + "RGB Values: R, G or B not interger" );
+                    }
+                String redGreenBlue = sx[ i ].substring( sx[ i ]
+                        .indexOf( CSSHandler.SEPARATOR ) + 1, sx[ i ]
+                        .length() );
+                Integer rgbValue = Integer.valueOf( redGreenBlue );
+                ix[ i ] = rgbValue.intValue();
+                }
+            else
+                {
+                throw new ODTException(
+                        "Error in DOM/style data externalization: "
+                                + "RGB Values: can't resolve value type" );
+                }
+            }
+        return new Color( ix[ 0 ], ix[ 1 ], ix[ 2 ] );
+        }
+
+    /**
+     * Parses the Value Type from Value.
+     *
+     * @param aAttrValue The attribute value
+     *
+     * @return Parsed value as a string
+     *
+     * @throws IOException Signals that an I/O exception has occurred.
+     * @throws ODTException
+     */
+    private String parseAttrValue( String aAttrValue, PropertyValue aPropValue )
+        throws IOException, ODTException
+        {
+        if ( aAttrValue.length() > 0 )
+            {
+            if ( aAttrValue.contains( "|" ) )
+                {
+                String type = aAttrValue
+                        .substring( 0, aAttrValue.indexOf( '|' ) );
+                String value = aAttrValue.substring(
+                        aAttrValue.indexOf( '|' ) + 1, aAttrValue.length() );
+
+                Short lexicalUnit = Short.valueOf( type );
+                Short primitiveValueType =
+                        iValueTypeResolver.getValue( lexicalUnit );
+
+                switch ( primitiveValueType.intValue() )
+                    {
+                    case ValueTypeResolver.E_NUMBER:
+                        double doubleValueInteger = Double.valueOf( value ).doubleValue();
+                        aPropValue.setRealValue( doubleValueInteger,
+                                                 primitiveValueType.shortValue() );
+                        break;
+                    case ValueTypeResolver.E_UNIT_VALUE:
+                    case ValueTypeResolver.E_PERCENTAGE:
+                    case ValueTypeResolver.E_EMS:
+                    case ValueTypeResolver.E_EXS:
+                    case ValueTypeResolver.E_PX:
+                    case ValueTypeResolver.E_CM:
+                    case ValueTypeResolver.E_MM:
+                    case ValueTypeResolver.E_IN:
+                    case ValueTypeResolver.E_PT:
+                    case ValueTypeResolver.E_PC:
+                    case ValueTypeResolver.E_DEG:
+                    case ValueTypeResolver.E_RAD:
+                    case ValueTypeResolver.E_GRAD:
+                    case ValueTypeResolver.E_MS:
+                    case ValueTypeResolver.E_S:
+                    case ValueTypeResolver.E_HZ:
+                    case ValueTypeResolver.E_KHZ:
+                        double doubleValuePercentage = Double.valueOf( value ).doubleValue();
+                        aPropValue.setRealValue( doubleValuePercentage,
+                                                 primitiveValueType.shortValue() );
+                        break;
+                    case ValueTypeResolver.E_IDENT:
+                    case ValueTypeResolver.E_STRING:
+                    case ValueTypeResolver.E_URI:
+                    case ValueTypeResolver.E_ATTR:
+                        aPropValue.setString( new String( value ),
+                                primitiveValueType.shortValue() );
+                        break;
+                    case ValueTypeResolver.E_RGB_COLOR:
+                        Color color = parseColorFromString(value);
+                        aPropValue.setRgbValue( color );
+                        break;
+                    default:
+                        break;
+                    }
+
+                return value;
+                }
+            return aAttrValue;
+            }
+        return aAttrValue;
+        }
+
+    /**
+     * Externalize a list of property values.
+     *
+     * @param aNode the a node
+     * @param aAttrList the a attr list
+     *
+     * @return true, if property is inherited
+     *
+     * @throws IOException if data output stream can not be written
+     * @throws ODTException if there is an error with externalizing property values
+     */
+    private boolean externalizePropertyValueList( Node aNode, NamedNodeMap aAttrList )
+        throws IOException, ODTException
+        {
+        PropertyValueList valueList = new PropertyValueList( iStringPool );
+
+        boolean isInherited = false;
+        String name = null;
+        String value = null;
+
+        for ( int i = 0; i < aAttrList.getLength(); i++ )
+            {
+            Node node = aAttrList.item( i );
+
+            if ( nodeType( node ) == ATTRIBUTE &&
+                 !node.getNodeName().equals( CSSDOMProcessor.STRING_PSEUDOCLASS ) )
+                {
+                String attrName = node.getNodeName();
+                String attrValue = node.getNodeValue();
+
+                if( attrName.equals( CSSDOMProcessor.STRING_NAME ) &&
+                    name == null )
+                    {
+                    name = attrValue;
+                    }
+                    else if( attrName.startsWith( CSSDOMProcessor.STRING_VALUE ) )
+                    {
+                    value = attrValue;
+
+                    // Checking if property is inherited.
+                    // This emulates the behavior of the Symbian side implementation
+                    // where this is true in following cases:
+                    // 1. Value equals "inherited"
+                    // 2. Property is inheritable and the element can inherit properties
+                    //
+                    // This means that also some properties that aren't actually
+                    // inherited, are also set with value isIherited == "true"
+                    isInherited = iCSSDOMProsessor.canInherit( (Element) aNode, name, value );
+
+                    PropertyValue propValue = valueList.newItem();
+                    parseAttrValue( value, propValue );
+                    }
+                else
+                    {
+                    throw new ODTException( "Error in DOM/style data externalization: " +
+                    		"Property values" );
+                    }
+                }
+            }
+
+        if( name != null &&
+            value != null )
+            {
+            // Write property name
+            int nameRef = iStringPool.addString( name );
+            iODTDos.writeInt16( nameRef );
+
+            // Externalize the property value list to the stream
+            valueList.externalize( iODTDos );
+            }
+        else
+            {
+            throw new ODTException( "Error in DOM/style data externalization: " +
+            		"Property values" );
+            }
+        return isInherited;
+        }
+
+    /**
+     * Count element nodes in a node list.
+     * @param aList Node list
+     * @param aNodeType Node type to count
+     * @return Count of found nodes
+     */
+    private static int countNodes( NodeList aList )
+        {
+        int count = 0;
+        for( int i = 0; i < aList.getLength(); i++ )
+            {
+            // Exclude elements that are style properties
+            if( nodeType( aList.item( i ) ) == NODE )
+                {
+                count++;
+                }
+            }
+
+        return count;
+        }
+
+    /**
+     * Count attributes in a named node map.
+     * @param aList Node map
+     * @param aNodeType Node type to count
+     * @return Count of found nodes
+     */
+    private static int countAttributes( NamedNodeMap aList )
+        {
+        int count = 0;
+        for( int i = 0; i < aList.getLength(); i++ )
+            {
+            if( nodeType( aList.item( i ) ) == ATTRIBUTE )
+                {
+                count++;
+                }
+            }
+
+        return count;
+        }
+
+    /**
+     * Count properties in a node list.
+     * @param aList Node list
+     * @param aNodeType Node type to count
+     * @return Count of found nodes
+     */
+    private static int countProperties( NodeList aList )
+        {
+        int count = 0;
+        for( int i = 0; i < aList.getLength(); i++ )
+            {
+            // Include only elements that are style properties
+            if( nodeType( aList.item( i ) ) == PROPERTY )
+                {
+                count++;
+                }
+            }
+
+        return count;
+        }
+
+    /**
+     * Resolve node type. Possible types: NODE, ATTRIBUTE or PROPERTY
+     * @param aItem Node to resolve
+     * @return Type of the node. If the type can not be determined, UNKNOWN is returned
+     */
+    private static int nodeType( Node aItem )
+        {
+        int type = UNKNOWN;
+
+        // Element that is not a style property element
+        if( aItem.getNodeType() == Node.ELEMENT_NODE &&
+            !aItem.getNodeName().equals( CSSDOMProcessor.STRING_PROPERTY ) )
+            {
+            type = NODE;
+            }
+        // Attribute nodes
+        else if( aItem.getNodeType() == Node.ATTRIBUTE_NODE )
+            {
+            type = ATTRIBUTE;
+            }
+        // Style property element
+        else if( aItem.getNodeType() == Node.ELEMENT_NODE &&
+                 aItem.getNodeName().equals( CSSDOMProcessor.STRING_PROPERTY ) )
+            {
+            type = PROPERTY;
+            }
+
+        return type;
+        }
+
+    }