themeinstaller/source/src/com/nokia/tools/themeinstaller/cssparser/CSSDOMProcessor.java
branchRCL_3
changeset 32 fe49e33862e2
parent 31 b685c59de105
child 33 04b7640f6fb5
equal deleted inserted replaced
31:b685c59de105 32:fe49e33862e2
     1 /*
       
     2 * Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies). 
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  CSSDOMProcessor applies given CSSRules to DOM Document.
       
    15  *
       
    16 */
       
    17 
       
    18 
       
    19 package com.nokia.tools.themeinstaller.cssparser;
       
    20 
       
    21 import java.util.Collections;
       
    22 import java.util.HashMap;
       
    23 import java.util.Iterator;
       
    24 import java.util.Vector;
       
    25 
       
    26 import org.w3c.css.sac.LexicalUnit;
       
    27 import org.w3c.dom.Document;
       
    28 import org.w3c.dom.Element;
       
    29 import org.w3c.dom.NamedNodeMap;
       
    30 import org.w3c.dom.Node;
       
    31 import org.w3c.dom.traversal.DocumentTraversal;
       
    32 import org.w3c.dom.traversal.NodeFilter;
       
    33 import org.w3c.dom.traversal.NodeIterator;
       
    34 
       
    35 /**
       
    36  * CSSDOMProcessor applies given CSSRules to DOM Document.
       
    37  */
       
    38 public class CSSDOMProcessor
       
    39     {
       
    40     /** The Constant STRING_PROPERTY. */
       
    41     public static final String STRING_PROPERTY = "styleproperty";
       
    42 
       
    43     /** The Constant STRING_PSEUDOCLASS. */
       
    44     public static final String STRING_PSEUDOCLASS = "pseudoclass";
       
    45 
       
    46     /** The Constant STRING_NAME. */
       
    47     public static final String STRING_NAME = "name";
       
    48 
       
    49     /** The Constant STRING_VALUE. */
       
    50     public static final String STRING_VALUE = "value";
       
    51 
       
    52     /** The Constant STRING_INHERIT. */
       
    53     public static final String STRING_INHERIT = "inherit";
       
    54 
       
    55     /** The Constant CHAR_COLON. */
       
    56     private static final char CHAR_COLON = ':';
       
    57 
       
    58     /** The Constant STRING_SEPARATOR. */
       
    59     private static final String STRING_SEPARATOR = "|";
       
    60 
       
    61     /** The Constant PSEUDO_TABLE. */
       
    62     private static final String[] UNSTYLABLE_ELEMENTS_TABLE = { "styleproperty" };
       
    63 
       
    64     /** The Element type resolver. */
       
    65     private static ElementTypeResolver iElementTypeResolver = new ElementTypeResolver();
       
    66 
       
    67     /** The Constant INHERITABLE_PROPERTIES. */
       
    68     public static final String[] INHERITABLE_PROPERTIES = { "visibility",
       
    69             "block-progression", "direction", "color", "font-family",
       
    70             "font-size", "font-weight", "font-style", "_s60-tab-style",
       
    71             "_s60-tab-color" };
       
    72 
       
    73     /** The Match Maker. */
       
    74     private CSSMatchMaker iCSSMatchMaker;
       
    75 
       
    76     /**
       
    77      * Instantiates a new CSSDOM processor.
       
    78      */
       
    79     public CSSDOMProcessor()
       
    80         {
       
    81         iCSSMatchMaker = new CSSMatchMaker();
       
    82         }
       
    83 
       
    84     /**
       
    85      * Sort rules.
       
    86      *
       
    87      * @param rules The rules to be sorted by priority
       
    88      */
       
    89     private static void sortRules( Vector rules )
       
    90         {
       
    91         Collections.sort( rules );
       
    92         }
       
    93 
       
    94     /**
       
    95      * Checks if is stylable element.
       
    96      *
       
    97      * @param aElement The element to be checked
       
    98      *
       
    99      * @return true, if is stylable element
       
   100      */
       
   101     private static boolean isStylableElement(Element aElement){
       
   102 
       
   103     for ( int i = 0; i < UNSTYLABLE_ELEMENTS_TABLE.length; i++ )
       
   104         {
       
   105         if ( UNSTYLABLE_ELEMENTS_TABLE[ i ].equals( aElement.getTagName() ))
       
   106             {
       
   107             return false;
       
   108             }
       
   109         }
       
   110 
       
   111         return true;
       
   112     }
       
   113 
       
   114     /**
       
   115      * Apply style rule to DOM. Walks through the DOM tree elements and finds
       
   116      * those that match with the CSSRule Selector. If matching element is found,
       
   117      * Rule is applied to DOM Element.
       
   118      *
       
   119      * @param aDocument The DOM document to apply the rules
       
   120      * @param aRuleList List of CSS style rules
       
   121      */
       
   122     public void applyRulesToDom( Document aDocument,
       
   123             Vector aRuleList )
       
   124         {
       
   125         DocumentTraversal traversal = ( DocumentTraversal ) aDocument;
       
   126 
       
   127         NodeIterator iterator = traversal.createNodeIterator( aDocument
       
   128                 .getDocumentElement(), NodeFilter.SHOW_ELEMENT, null, true );
       
   129 
       
   130         for ( Node node = iterator.nextNode(); node != null; node = iterator
       
   131                 .nextNode() )
       
   132             {
       
   133             Element element = ( Element ) node;
       
   134                 if ( isStylableElement(element) )
       
   135                 {
       
   136                 Vector rulesMatch = new Vector();
       
   137 
       
   138                 for ( int i = 0; i < aRuleList.size(); i++ )
       
   139                     {
       
   140                 	
       
   141                     CSSRule rule = ( CSSRule ) aRuleList.get( i );
       
   142                     
       
   143                     if ( iCSSMatchMaker.match( rule, element ) )
       
   144                         {
       
   145                         rulesMatch.add( rule );
       
   146                         }
       
   147                     }
       
   148 
       
   149                 sortRules( rulesMatch );
       
   150 
       
   151                 for ( int j = 0; j < rulesMatch.size(); j++ )
       
   152                     {
       
   153                     CSSRule matchingRule = ( CSSRule ) rulesMatch.elementAt( j );
       
   154 //                    if(matchingRule.getSelector().toString().equals("box:edit")){
       
   155 //                    	System.out.println("Party");
       
   156 //                    }
       
   157                     
       
   158                     applyRuleToElement( element, matchingRule );
       
   159                     }
       
   160                 }
       
   161             }
       
   162             applyInheritance( aDocument );
       
   163         }
       
   164 
       
   165 
       
   166     /**
       
   167      * Checks if given property is inheritable property.
       
   168      *
       
   169      * @param aProperty the a property
       
   170      *
       
   171      * @return true, if is inheritable property
       
   172      */
       
   173     public boolean isInheritableProperty( String aProperty )
       
   174         {
       
   175         for ( int i = 0; i < INHERITABLE_PROPERTIES.length; i++ )
       
   176             {
       
   177             if ( aProperty.equals( INHERITABLE_PROPERTIES[ i ] ) )
       
   178                 {
       
   179                 return true;
       
   180                 }
       
   181             }
       
   182         return false;
       
   183         }
       
   184 
       
   185     /**
       
   186      * Checks if given element can inherit given property name.
       
   187      *
       
   188      * @param aElement the a element
       
   189      * @param aPropertyName the a value's name
       
   190      *
       
   191      * @return true, if element can inherit the property
       
   192      */
       
   193     public boolean canInherit( Element aElement, String aPropertyName )
       
   194         {
       
   195         return canInherit( aElement, aPropertyName, null );
       
   196         }
       
   197 
       
   198     /**
       
   199      * Checks if given element can inherit given property.
       
   200      *
       
   201      * If property value equals "inherit", the property
       
   202      * is interpreted as inherited.
       
   203      *
       
   204      * If property is inheritable and element can inherit
       
   205      * properties, the element can inherit the property.
       
   206      *
       
   207      * @param aElement the a element
       
   208      * @param aPropertyName the a property's name
       
   209      * @param aPropertyValue the a property's value
       
   210      *
       
   211      * @return true, if element can inherit the property
       
   212      */
       
   213     public boolean canInherit( Element aElement, String aPropertyName,
       
   214             String aPropertyValue )
       
   215         {
       
   216         if ( aPropertyValue != null && aPropertyValue.equals( STRING_INHERIT ) )
       
   217             {
       
   218             return true;
       
   219             }
       
   220 
       
   221         if ( !isInheritableProperty( aPropertyName ) )
       
   222             {
       
   223             return false;
       
   224             }
       
   225 
       
   226         if ( iElementTypeResolver.canInherit( aElement.getNodeName() ) )
       
   227             {
       
   228             return true;
       
   229             }
       
   230 
       
   231         return false;
       
   232         }
       
   233 
       
   234     /**
       
   235      * Apply inheritance for stylable nodes that can inherit properties from
       
   236      * parent elements.
       
   237      *
       
   238      * @param aDocument The DOM document to be modified
       
   239      */
       
   240     private void applyInheritance( Document aDocument )
       
   241         {
       
   242         DocumentTraversal traversal = ( DocumentTraversal ) aDocument;
       
   243 
       
   244         NodeIterator iterator = traversal.createNodeIterator( aDocument
       
   245                 .getDocumentElement(), NodeFilter.SHOW_ELEMENT, null, true );
       
   246 
       
   247         for ( Node node = iterator.nextNode(); node != null; node = iterator
       
   248                 .nextNode() )
       
   249             {
       
   250             Element element = ( Element ) node;
       
   251 //            if(node.getLocalName().equals("box"))
       
   252 //            		System.out.println("Kill me!!!");
       
   253             if ( isStylableElement( element ) )
       
   254                 {
       
   255                 if ( iElementTypeResolver.canInherit( element.getNodeName() ) )
       
   256                     {
       
   257                     applyInheritance( element );
       
   258                     }
       
   259                 }
       
   260             }
       
   261         }
       
   262 
       
   263     /**
       
   264      * Apply inheritance for DOM Element. Checks if any of the elements parents
       
   265      * has properties that can be inherited in the given element. If such
       
   266      * property is found, new property to the element is added, unless there
       
   267      * already exists a property with the same name.
       
   268      *
       
   269      * @param aElement The element to be modified
       
   270      */
       
   271     private void applyInheritance( Element aElement )
       
   272         {
       
   273         for ( int i = 0; i < INHERITABLE_PROPERTIES.length; i++ )
       
   274             {
       
   275             String inheritableProperty = INHERITABLE_PROPERTIES[ i ];
       
   276             if ( iElementTypeResolver.canInherit( aElement.getNodeName() ) )
       
   277                 {
       
   278                 if ( !hasAttribute( aElement, inheritableProperty ) )
       
   279                     {
       
   280                     if ( hasParentElementWithAttribute( aElement,
       
   281                             inheritableProperty ) )
       
   282                         {
       
   283                         if ( hasChildElementWithAttribute( aElement,
       
   284                                 STRING_PROPERTY, inheritableProperty ) == null )
       
   285                             {
       
   286                             addNewChildElement( aElement, STRING_PROPERTY,
       
   287                                     inheritableProperty,
       
   288                                     LexicalUnit.SAC_IDENT
       
   289                                             + STRING_SEPARATOR + STRING_INHERIT );
       
   290                             }
       
   291                         }
       
   292                     }
       
   293                 }
       
   294             }
       
   295         }
       
   296 
       
   297     /**
       
   298      * Checks for parent element with attribute name.
       
   299      *
       
   300      * @param aElement The element that's parents are checked
       
   301      * @param aAttributeName The name of the property
       
   302      *
       
   303      * @return true, aElement has parent node with given attribute name
       
   304      */
       
   305     private static boolean hasParentElementWithAttribute( Element aElement,
       
   306             String aAttributeName )
       
   307         {
       
   308         Node n = aElement.getParentNode();
       
   309         while ( n != null )
       
   310             {
       
   311             if ( n.getNodeType() == Node.ELEMENT_NODE )
       
   312                 {
       
   313                 if ( hasChildElementWithAttribute( ( Element ) n,
       
   314                         STRING_PROPERTY, aAttributeName ) != null )
       
   315                     {
       
   316                     return true;
       
   317                     }
       
   318                 }
       
   319             n = n.getParentNode();
       
   320             }
       
   321         return false;
       
   322         }
       
   323 
       
   324     /**
       
   325      * Apply rule to DOM Element. Creates child elements for aElement with style
       
   326      * data. If the element already has matching child element, the value of it
       
   327      * is overwritten.
       
   328      *
       
   329      * @param aElement The DOM Element
       
   330      * @param aRule CSS style rule
       
   331      */
       
   332     private void applyRuleToElement( Element aElement, CSSRule aRule )
       
   333         {
       
   334         HashMap styleMap = aRule.getStyleMap();
       
   335 
       
   336         Iterator itKeys = styleMap.keySet().iterator();
       
   337         while ( itKeys.hasNext() )
       
   338             {
       
   339             String keyName = ( String ) itKeys.next();
       
   340 
       
   341             // Case 1 : Rule has pseudo selector, add or replace Element
       
   342             if ( aRule.isPseudo() )
       
   343                 {
       
   344                 String selectorString = aRule.getSelector().toString();
       
   345                 String pseudoPart = selectorString.substring( selectorString
       
   346                         .lastIndexOf( CHAR_COLON ) + 1 );
       
   347 
       
   348                 Vector elementsAttributes = new Vector();
       
   349                 elementsAttributes.add( new NameValuePair(STRING_NAME, keyName) );
       
   350                 elementsAttributes.add( new NameValuePair(STRING_PSEUDOCLASS, pseudoPart) );
       
   351 
       
   352                 Element e = hasChildElementWithAttributes( aElement,
       
   353                         STRING_PROPERTY, elementsAttributes );
       
   354                 if ( e != null && hasAttribute( e, STRING_NAME, keyName ) )
       
   355                     {
       
   356 
       
   357                     CSSStyleProperty property = ( CSSStyleProperty ) styleMap
       
   358                             .get( keyName );
       
   359                     Vector values = property.getValues();
       
   360 
       
   361                     CSSPropertyValue propertyValue = ( CSSPropertyValue ) values
       
   362                             .elementAt( 0 );
       
   363 
       
   364                     // 1st value has name "value"
       
   365                     e.setAttribute( STRING_VALUE, propertyValue
       
   366                             .getValueTypeAndValue() );
       
   367 
       
   368                     // if there are more values, their name is set to "value1,
       
   369                     // value 2, ..."
       
   370                     for ( int i = 1; i < values.size(); i++ )
       
   371                         {
       
   372 
       
   373                         propertyValue = ( CSSPropertyValue ) values
       
   374                                 .elementAt( i );
       
   375 
       
   376                         e.setAttribute( STRING_VALUE + i, propertyValue
       
   377                                 .getValueTypeAndValue() );
       
   378                         }
       
   379                     }
       
   380                 else
       
   381                     {
       
   382 
       
   383                     CSSStyleProperty property = ( CSSStyleProperty ) styleMap
       
   384                             .get( keyName );
       
   385                     Vector values = property.getValues();
       
   386 
       
   387                     CSSPropertyValue propertyValue = ( CSSPropertyValue ) values
       
   388                             .elementAt( 0 );
       
   389 
       
   390                     Element newElement = addNewChildElement( aElement,
       
   391                             STRING_PROPERTY, keyName, propertyValue
       
   392                                     .getValueTypeAndValue(), pseudoPart );
       
   393 
       
   394                     // Rest of the values are set to element we just created
       
   395                     for ( int i = 1; i < values.size(); i++ )
       
   396                         {
       
   397                         propertyValue = ( CSSPropertyValue ) values
       
   398                                 .elementAt( i );
       
   399                         newElement.setAttribute( STRING_VALUE + i,
       
   400                                 propertyValue.getValueTypeAndValue() );
       
   401                         }
       
   402                     }
       
   403                 }
       
   404             // Case 2 : Rule don't have pseudo selector, add or replace Element
       
   405             else
       
   406                 {
       
   407                 Vector elementsAttributes = new Vector();
       
   408                 elementsAttributes.add( new NameValuePair(STRING_NAME, keyName) );
       
   409 
       
   410                 Element e = hasChildElementWithAttributes( aElement,
       
   411                         STRING_PROPERTY, elementsAttributes );
       
   412 
       
   413                 if ( e != null && hasAttribute( e, STRING_NAME, keyName ) )
       
   414                     {
       
   415                     CSSStyleProperty property = ( CSSStyleProperty ) styleMap
       
   416                             .get( keyName );
       
   417                     Vector values = property.getValues();
       
   418 
       
   419                     CSSPropertyValue propertyValue = ( CSSPropertyValue ) values
       
   420                             .elementAt( 0 );
       
   421 
       
   422                     // 1st value has name "value"
       
   423                     e.setAttribute( STRING_VALUE, propertyValue
       
   424                             .getValueTypeAndValue() );
       
   425 
       
   426                     // if there are more values, their name is set to "value1,
       
   427                     // value 2, ..."
       
   428                     for ( int i = 1; i < values.size(); i++ )
       
   429                         {
       
   430                         propertyValue = ( CSSPropertyValue ) values
       
   431                                 .elementAt( i );
       
   432                         e.setAttribute( STRING_VALUE + i, propertyValue
       
   433                                 .getValueTypeAndValue() );
       
   434                         }
       
   435                     }
       
   436                 else
       
   437                     {
       
   438 
       
   439                     CSSStyleProperty property = ( CSSStyleProperty ) styleMap
       
   440                             .get( keyName );
       
   441                     Vector values = property.getValues();
       
   442 
       
   443                     CSSPropertyValue propertyValue = ( CSSPropertyValue ) values
       
   444                             .elementAt( 0 );
       
   445 
       
   446                     Element newElement = addNewChildElement( aElement,
       
   447                             STRING_PROPERTY, keyName, propertyValue
       
   448                                     .getValueTypeAndValue() );
       
   449 
       
   450                     // Rest of the values are set to element we just created
       
   451                     for ( int i = 1; i < values.size(); i++ )
       
   452                         {
       
   453                         propertyValue = ( CSSPropertyValue ) values
       
   454                                 .elementAt( i );
       
   455                         newElement.setAttribute( STRING_VALUE + i,
       
   456                                 propertyValue.getValueTypeAndValue() );
       
   457                         }
       
   458                     }
       
   459                 }
       
   460             }
       
   461         }
       
   462 
       
   463     /**
       
   464      * Checks if DOM Element has a attribute with given value.
       
   465      *
       
   466      * @param aElement The DOM element to check
       
   467      * @param aValue Attributes value
       
   468      *
       
   469      * @return true, if successful
       
   470      */
       
   471     private static boolean hasAttribute( Element aElement, String aValue )
       
   472         {
       
   473         return hasAttribute( aElement, null, aValue );
       
   474         }
       
   475 
       
   476 
       
   477     /**
       
   478      * Checks if DOM Element has a attribute with given name and value.
       
   479      * Attribute's name can be null, when only the attributes value is checked
       
   480      *
       
   481      * @param aElement The DOM element to check
       
   482      * @param aName Attributes name
       
   483      * @param aValue Attributes value
       
   484      *
       
   485      * @return true, if successful
       
   486      */
       
   487     private static boolean hasAttribute( Element aElement, String aName,
       
   488             String aValue )
       
   489         {
       
   490         if ( aElement == null )
       
   491             {
       
   492             return false;
       
   493             }
       
   494         if ( aElement.hasAttributes() )
       
   495             {
       
   496             NamedNodeMap nnm = aElement.getAttributes();
       
   497             for ( int i = 0; i < nnm.getLength(); i++ )
       
   498                 {
       
   499                 Node att = nnm.item( i );
       
   500                 if ( aName != null )
       
   501                     {
       
   502                     if ( att.getNodeValue().equals( aValue )
       
   503                             && att.getNodeName().equals( aName ) )
       
   504                         {
       
   505                         return true;
       
   506                         }
       
   507                     }
       
   508                 else
       
   509                     {
       
   510                     if ( att.getNodeValue().equals( aValue ) )
       
   511                         {
       
   512                         return true;
       
   513                         }
       
   514                     }
       
   515                 }
       
   516             }
       
   517         return false;
       
   518         }
       
   519 
       
   520     /**
       
   521      * Checks for elements attributes.
       
   522      *
       
   523      * @param aElement The element to be checked
       
   524      * @param aAttributes Attributes in name-value pairs
       
   525      *
       
   526      * @return true, if element has attributes given in aAttributes
       
   527      */
       
   528     private static boolean hasAttributes( Element aElement, Vector aAttributes )
       
   529         {
       
   530         if ( aElement == null )
       
   531             {
       
   532             return false;
       
   533             }
       
   534 
       
   535         if ( aElement.hasAttributes() )
       
   536             {
       
   537 
       
   538             for ( int i = 0; i < aAttributes.size(); i++ )
       
   539                 {
       
   540 
       
   541                 NameValuePair nameValuePair = ( NameValuePair ) aAttributes
       
   542                         .get( i );
       
   543 
       
   544                 String name = nameValuePair.getName();
       
   545                 String value = nameValuePair.getValue();
       
   546 
       
   547                 if ( !hasAttribute( aElement, name, value ) )
       
   548                     {
       
   549                     return false;
       
   550                     }
       
   551                 }
       
   552             }
       
   553         return true;
       
   554         }
       
   555 
       
   556     /**
       
   557      * Checks for child element with attribute's name.
       
   558      *
       
   559      * @param aParent The parent element
       
   560      * @param aElementName The element tag name to be matched with child nodes
       
   561      *            name
       
   562      * @param aName Attributes name to be matched
       
   563      *
       
   564      * @return Child element with attribute, null if none
       
   565      */
       
   566     private static Element hasChildElementWithAttribute( Element aParent,
       
   567             String aElementName, String aName )
       
   568         {
       
   569         for ( Node n = aParent.getFirstChild(); n != null; n = n
       
   570                 .getNextSibling() )
       
   571             {
       
   572             if ( n.getNodeType() == Node.ELEMENT_NODE )
       
   573                 {
       
   574                 if ( hasAttribute( ( Element ) n, aName )
       
   575                         && n.getNodeName().equals( aElementName ) )
       
   576                     {
       
   577                     return ( Element ) n;
       
   578                     }
       
   579                 }
       
   580             }
       
   581         return null;
       
   582         }
       
   583 
       
   584 
       
   585 
       
   586     /**
       
   587      * Checks for child element with attributes.
       
   588      *
       
   589      * @param aParent The parent element
       
   590      * @param aElementName The element tag name to be matched with child nodes
       
   591      *            name
       
   592      * @param aNameValuePairs The Name-Value pairs for attributes that need to be
       
   593      *            found
       
   594      *
       
   595      * @return Child element with attributes, null if none
       
   596      */
       
   597     private static Element hasChildElementWithAttributes( Element aParent,
       
   598             String aElementName, Vector aNameValuePairs )
       
   599         {
       
   600         for ( Node n = aParent.getFirstChild(); n != null; n = n
       
   601                 .getNextSibling() )
       
   602             {
       
   603             if ( n.getNodeType() == Node.ELEMENT_NODE )
       
   604                 {
       
   605                 if ( n.getNodeName().equals( aElementName )
       
   606                         && hasAttributes( ( Element ) n, aNameValuePairs ) )
       
   607                     {
       
   608                     return ( Element ) n;
       
   609                     }
       
   610                 }
       
   611             }
       
   612         return null;
       
   613         }
       
   614 
       
   615     /**
       
   616      * Adds the new node to DOM.
       
   617      *
       
   618      * @param aParent The parent of the new node
       
   619      * @param aElementName Element name for new DOM node
       
   620      * @param aKeyName Attribute name for new DOM node
       
   621      * @param aKeyValue Attribute value for new DOM node
       
   622      */
       
   623     private Element addNewChildElement( Element aParent,
       
   624             String aElementName, String aKeyName, String aKeyValue )
       
   625         {
       
   626         return addNewChildElement( aParent, aElementName, aKeyName, aKeyValue, null );
       
   627         }
       
   628 
       
   629     /**
       
   630      * Adds the new node to DOM.
       
   631      *
       
   632      * @param aParent The parent of the new node
       
   633      * @param aElementName Element name for new DOM node
       
   634      * @param aKeyName Attribute name for new DOM node
       
   635      * @param aKeyValue Attribute value for new DOM node
       
   636      * @param aPseudo Pseudo attribute value for the DOM node
       
   637      */
       
   638     private Element addNewChildElement( Element aParent, String aElementName,
       
   639             String aKeyName, String aKeyValue, String aPseudo )
       
   640         {
       
   641         Document document = aParent.getOwnerDocument();
       
   642 
       
   643         String namespaceUri = aParent.getNamespaceURI();
       
   644 
       
   645         Element newElement = document.createElementNS( namespaceUri,
       
   646                 aElementName );
       
   647 
       
   648         if ( aPseudo != null )
       
   649             {
       
   650             newElement.setAttribute( STRING_PSEUDOCLASS, aPseudo );
       
   651             }
       
   652         newElement.setAttribute( STRING_NAME, aKeyName );
       
   653         newElement.setAttribute( STRING_VALUE, aKeyValue );
       
   654 
       
   655         aParent.appendChild( newElement );
       
   656         return newElement;
       
   657         }
       
   658 
       
   659 
       
   660     /**
       
   661      * Class for temporarily store Node's attribute name and value.
       
   662      */
       
   663     private class NameValuePair
       
   664         {
       
   665         private String iName;
       
   666 
       
   667         private String iValue;
       
   668 
       
   669         NameValuePair( String aName, String aValue )
       
   670             {
       
   671             iName = aName;
       
   672             iValue = aValue;
       
   673             }
       
   674 
       
   675         public String getName()
       
   676             {
       
   677             return iName;
       
   678             }
       
   679 
       
   680         public String getValue()
       
   681             {
       
   682             return iValue;
       
   683             }
       
   684 
       
   685         }
       
   686 
       
   687     }