themeinstaller/source/src/com/nokia/tools/themeinstaller/odtconverter/DOMExternalizer.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/themeinstaller/source/src/com/nokia/tools/themeinstaller/odtconverter/DOMExternalizer.java Tue Feb 02 00:15:44 2010 +0200
@@ -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;
+ }
+
+ }