You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by bs...@apache.org on 2013/07/17 05:00:30 UTC

svn commit: r1503980 - in /myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style: cache/ util/ xml/parse/

Author: bsullivan
Date: Wed Jul 17 03:00:30 2013
New Revision: 1503980

URL: http://svn.apache.org/r1503980
Log:
[Trinidad]TRINIDAD-2401 Clean up skinning code to avoid use of obsolete synchronized classes

The skinning code currently uses several obsolete implementation class--Vector, Stack, HashTable that are implicitly synchronized. None of this synchronization is necessary. Replaces these classes with their modern equivalents. In addition, much of the code is confusing due to functions performing multiple different tasks simultaneously. Break these up into easy to understand pieces that perform a single task.

Modified:
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSGenerationUtils.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/NameUtils.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/StyleWriterFactory.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java?rev=1503980&r1=1503979&r2=1503980&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java Wed Jul 17 03:00:30 2013
@@ -389,12 +389,15 @@ public class FileSystemStyleCache implem
       // to a new re-allocated cache.
       if (_cache == null)
         _cache = new ConcurrentHashMap<Key, Entry>();
+      
       if (_entryCache == null)
         _entryCache = new ConcurrentHashMap<Object, Entry>(19);
+      
       if (_reusableStyleMap == null)
-        _reusableStyleMap = new ConcurrentHashMap<Style, Style>();
+        _reusableStyleMap = new ConcurrentHashMap<UnmodifiableStyle, UnmodifiableStyle>();
+      
       if (_reusableSelectorMap == null)
-        _reusableSelectorMap = new ConcurrentHashMap<Selector, Selector>();
+        _reusableSelectorMap = new ConcurrentHashMap<String, Selector>();
 
       cache = _cache;
       entryCache = _entryCache;
@@ -490,6 +493,31 @@ public class FileSystemStyleCache implem
   }
 
   /**
+   * Returns the Selector to use for a selector String, possibly returning a cached Selector rather
+   * than creating a new one.  As Selectors are created, they are added to the cache.
+   * 
+   * @param selectorString
+   * @return the Selector to use for that Selector String
+   */
+  private Selector _getSelector(String selectorString)
+  {
+    Selector cachedSelector = _reusableSelectorMap.get(selectorString);
+    
+    if (cachedSelector != null)
+    {
+      return cachedSelector;
+    }
+    else
+    {
+      Selector selectorCreated = Selector.createSelector(selectorString);
+      
+      _reusableSelectorMap.put(selectorString, selectorCreated);
+      
+      return selectorCreated;
+    }
+  }
+  
+  /**
    * Creates and caches an Entry for the specified StyleContext
    * This generates a style sheet for the specific StyleContext
    * (locale, direction, etc), and puts that style sheet's uri in the Entry.
@@ -515,36 +543,32 @@ public class FileSystemStyleCache implem
     // If a selector has no properties at all (af|foo {}), it will not be returned in the list of
     // StyleNodes. It gets into the shortStyleClassMap which has already happened, but
     // it won't get written to the CSS File.
-    StyleNode[] styleNodes = _getStyleContextResolvedStyles(context, document);
-    if (styleNodes == null)
+    List<StyleNode> styleNodes = _getStyleContextResolvedStyles(context, document);
+    
+    if (styleNodes.isEmpty())
       return null;
 
-    /* This code fills in the <Selector, Style> resolvedSelectorStyleMap map. 
-     * We use _reusableStyleMap to reuse the Style objects when possible
-     * since we have a large number of Style objects. */
+    // This code fills in the <Selector, Style> resolvedSelectorStyleMap map. 
+    // We use _reusableStyleMap to reuse the Style objects when possible
+    // since we have a large number of Style objects.
     ConcurrentMap<Selector, Style> resolvedSelectorStyleMap = null;
-    for (int i=0; i < styleNodes.length; i++)
+    
+    for (StyleNode styleNode : styleNodes)
     {
-      String selector = styleNodes[i].getSelector();
-      if (selector != null)
+      String selectorString = styleNode.getSelector();
+      
+      if (selectorString != null)
       {
-        Style style = _convertStyleNodeToStyle(styleNodes[i], _reusableStyleMap);
+        Style style = _convertStyleNodeToStyle(styleNode, _reusableStyleMap);
+        
         if (resolvedSelectorStyleMap == null)
           resolvedSelectorStyleMap = new ConcurrentHashMap<Selector, Style>();
 
         // To save memory, we reuse Selector objects
-        Selector selectorCreated = Selector.createSelector(selector);
-        Selector cachedSelector = _reusableSelectorMap.get(selectorCreated);
-        if (cachedSelector == null)
-        {
-          _reusableSelectorMap.put(selectorCreated, selectorCreated);
-          resolvedSelectorStyleMap.put(selectorCreated, style);
-        }
-        else
-        {
-          resolvedSelectorStyleMap.put(cachedSelector, style);
-        }
+        Selector selector = _getSelector(selectorString);
         
+        // cache the Style under the Selector
+        resolvedSelectorStyleMap.put(selector, style);        
       }
     }
     
@@ -687,24 +711,24 @@ public class FileSystemStyleCache implem
    * This will be those StyleNodes that match the locale, direction, browser, etc -- the
    * info that is in the StyleContext.
    */
-  private StyleNode[] _getStyleContextResolvedStyles(
+  private List<StyleNode> _getStyleContextResolvedStyles(
     StyleContext context,
     StyleSheetDocument document
     )
   {
-    Iterator<StyleNode> e = document.getStyles(context);
-    if ((e == null) || !e.hasNext())
+    Iterator<StyleNode> styleNodes = document.getStyles(context);
+    
+    if ((styleNodes == null) || !styleNodes.hasNext())
     {
       if (_LOG.isWarning())
         _LOG.warning("NO_STYLES_FOUND_CONTEXT", context);
-      return null;
+      
+      return Collections.emptyList();
+    }
+    else
+    {
+      return CollectionUtils.arrayList(styleNodes);
     }
-
-    List<StyleNode> v = new ArrayList<StyleNode>();
-    while (e.hasNext())
-      v.add(e.next());
-
-    return v.toArray(new StyleNode[v.size()]);
   }
 
   /**
@@ -712,30 +736,40 @@ public class FileSystemStyleCache implem
    * styleSheetNodes that have been filtered from the StyleContext and StyleSheetDocument.
    */
   private ConcurrentMap<Object, Object> _getStyleContextResolvedSkinProperties(
-    StyleNode[] styleNodes
+    List<StyleNode> styleNodes
     )
   {
     // Use the resolved StyleNode[] to get the skinProperties from them.
     ConcurrentMap<Object, Object> skinPropertiesMap = new ConcurrentHashMap<Object, Object>();
+    
+    StringBuilder keyBuilder = new StringBuilder();
+    
     for (StyleNode styleNode : styleNodes)
     {
       Collection<PropertyNode> skinPropertyNodes = styleNode.getSkinProperties();
+      
       for (PropertyNode node : skinPropertyNodes)
       {
+        // Store the property selector + property Name as the Skin Property Key.
+        // e.g., use af|breadCrumbs-tr-show-last-item
+        
         // create SkinProperty key and SkinProperty value
         String selectorName = styleNode.getSelector();
+        
         if (selectorName == null)
         {
-          selectorName = "." + styleNode.getName() + ":alias";
+          keyBuilder.append('.').append(styleNode.getName()).append(":alias");
+        }
+        else
+        {
+          keyBuilder.append(selectorName);         
         }
+        
         String name = node.getName();
 
-        // Store the property selector + property Name as the Skin Property Key.
-        // e.g., use af|breadCrumbs-tr-show-last-item
-        StringBuilder keyBuilder = new StringBuilder(selectorName.length() + name.length());
-        keyBuilder.append(selectorName);
         keyBuilder.append(name);
         String key = keyBuilder.toString();
+        keyBuilder.setLength(0);  // reset the StringBuilder
 
         // look up in map to get conversion
         Class<?> type = SkinProperties.PROPERTY_CLASS_TYPE_MAP.get(key);
@@ -756,7 +790,6 @@ public class FileSystemStyleCache implem
         }
         
         skinPropertiesMap.put(key, (propValueObj != null ? propValueObj : value));
-
       }
     }
     return skinPropertiesMap;
@@ -792,7 +825,7 @@ public class FileSystemStyleCache implem
   private List<String> _createStyleSheetFiles(
     StyleContext        context,
     StyleSheetDocument  document,
-    StyleNode[]         styles,
+    List<StyleNode>     styleNodes,
     Map<String, String> shortStyleClassMap,
     String[]            namespacePrefixes,
     boolean             checkModified,
@@ -843,16 +876,13 @@ public class FileSystemStyleCache implem
       getTargetStyleSheetName(context, document));
     CSSGenerationUtils.writeCSS(context,
                                 skin.getStyleSheetName(),
-                                styles,
+                                styleNodes,
                                 writerFactory,
                                 compressStyles,
                                 shortStyleClassMap,
                                 namespacePrefixes,
-                                _STYLE_KEY_MAP
-                                );
-
-    writerFactory.close();
-
+                                _STYLE_KEY_MAP);
+    
     // Return the name of the new style sheet
     return _getFileNames(writerFactory.getFiles());
   }
@@ -1192,52 +1222,77 @@ public class FileSystemStyleCache implem
     // of style classes and not the style class itself.
     return _SHORT_CLASS_PREFIX + Integer.toString(count, Character.MAX_RADIX);
   }
-
   
   /**
-   * Given a StyleNode object, which is an internal API that denotes a Style object
-   * with additional information like includedSelectors, create a simple public
-   * Style object which will be used in the SelectorStyleMap. When this method is called,
-   * the StyleNode object is already resolved (included selectors have been merged in)
-   * so that all the css properties are there.
+   * Convert a styleNode to a Map of CSS property names and values
    * @param styleNode
-   * @param reusableStyleMap A Map<Style, Style>. This is 
-   *  used so that we can reuse Style objects in StylesImpl if they have the same list of style property
-   *  names and values.
-   * @return A Style object created from the information in the styleNode. We reuse
-   *  Style objects if the properties are the same.
+   * @return
    */
-  public Style _convertStyleNodeToStyle(
-    StyleNode          styleNode, 
-    Map<Style, Style>  reusableStyleMap)
+  private Map<String, String> _styleNodeToMap(StyleNode styleNode)
   {
     // Add in the properties for the style; PropertyNode interns the 'name' and the most common 'value's.
     Collection<PropertyNode> propertyNodeList = styleNode.getProperties();
-    Map<String, String> styleProperties = new ArrayMap<String, String>(propertyNodeList.size());
+    
+    int propertyCount = propertyNodeList.size();
+    
+    Map<String, String> styleProperties = new ArrayMap<String, String>(propertyCount);
 
     for (PropertyNode property : propertyNodeList)
     {
       String name = property.getName();
+      
       String value = property.getValue();
+      
       if (name != null && value != null)
       {
         styleProperties.put(name, value);
       }
     }
 
+    return styleProperties;    
+  }
+
+  private UnmodifiableStyle _styleMapToUnmodifiableStyle(
+    Map<String, String> styleProperties,
+    Map<UnmodifiableStyle, UnmodifiableStyle> reusableStyleMap)
+  {
     // To save memory, we reuse Style objects for each FileSystemStyleCache instance.
-    Style style = new UnmodifiableStyle(styleProperties);
-    Style cachedStyle = reusableStyleMap.get(style);
-    if (cachedStyle == null)
+    UnmodifiableStyle style = new UnmodifiableStyle(styleProperties);
+    
+    UnmodifiableStyle cachedStyle = reusableStyleMap.get(style);
+    
+    if (cachedStyle != null)
     {
-      reusableStyleMap.put(style, style);
-      return style;         
+      return cachedStyle;
     }
     else
     {
-      return cachedStyle;
+      reusableStyleMap.put(style, style);
+      return style;         
     }
   }
+
+  /**
+   * Given a StyleNode object, which is an internal API that denotes a Style object
+   * with additional information like includedSelectors, create a simple public
+   * Style object which will be used in the SelectorStyleMap. When this method is called,
+   * the StyleNode object is already resolved (included selectors have been merged in)
+   * so that all the css properties are there.
+   * @param styleNode
+   * @param reusableStyleMap A Map<Style, Style>. This is 
+   *  used so that we can reuse Style objects in StylesImpl if they have the same list of style property
+   *  names and values.
+   * @return A Style object created from the information in the styleNode. We reuse
+   *  Style objects if the properties are the same.
+   */
+  private Style _convertStyleNodeToStyle(
+    StyleNode                                  styleNode, 
+    Map<UnmodifiableStyle, UnmodifiableStyle>  reusableStyleMap)
+  {
+    Map<String, String> styleProperties = _styleNodeToMap(styleNode);
+    
+    return _styleMapToUnmodifiableStyle(styleProperties, reusableStyleMap);
+  }
   
   /**
    * Key class used for hashing style sheet URIs. This key for the Entry
@@ -1560,13 +1615,11 @@ public class FileSystemStyleCache implem
     private final boolean              _compress;
   }
 
-  private class StyleWriterFactoryImpl
-    implements StyleWriterFactory
+  private class StyleWriterFactoryImpl implements StyleWriterFactory
   {
     private final String _outputDirectory;
     private final String _baseFilename;
-    private PrintWriter _out;
-    private List<File> _files = new LinkedList<File>();
+    private final List<File> _files = new LinkedList<File>();
 
     StyleWriterFactoryImpl(String outputDirectory, String baseName)
     {
@@ -1579,30 +1632,20 @@ public class FileSystemStyleCache implem
       return _files;
     }
 
+    /**
+     * Creates a new PrintWriter.  The caller is responsible for closing the Writer when done with it
+     * @return
+     */
     public PrintWriter createWriter()
     {
-      if (_out != null)
-      {
-        _out.close();
-      }
-
       File outputFile = _getOutputFile(_baseFilename, _files.size() + 1);
+      
       // We never want to do anything other than read it or delete it:
       outputFile.setReadOnly();
 
       _files.add(outputFile);
-      _out = _getWriter(outputFile);
-
-      return _out;
-    }
-
-    void close()
-    {
-      if (_out != null)
-      {
-        _out.close();
-        _out = null;
-      }
+      
+      return _getWriter(outputFile);
     }
   }
   
@@ -1615,12 +1658,12 @@ public class FileSystemStyleCache implem
    *  which holds on to many Style objects, it reduces the memory consumption by about half
    *  if we reuse Style objects per FileSystemStyleCache instance rather than per
    *  FileSystemStyleCache$Entry instance. The is the map we use to store unique Style objects. */
-  private ConcurrentMap<Style, Style> _reusableStyleMap;
+  private ConcurrentMap<UnmodifiableStyle, UnmodifiableStyle> _reusableStyleMap;
   
   /** Use this to store Selector objects so that they can be reused in all the FileSystemStyleCache$StylesImpl
    * objects. A generated css file can contain 4533 selectors at 16 bytes each. The Selectors will largely
    * be the same between FileSystemStyleCache$StylesImpl instances, so they should be shared. */
-  private ConcurrentMap<Selector, Selector> _reusableSelectorMap;
+  private ConcurrentMap<String, Selector> _reusableSelectorMap;
   
   /** The cache of style sheet URIs */
   private ConcurrentMap<Key, Entry> _cache;

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSGenerationUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSGenerationUtils.java?rev=1503980&r1=1503979&r2=1503980&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSGenerationUtils.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSGenerationUtils.java Wed Jul 17 03:00:30 2013
@@ -20,19 +20,20 @@ package org.apache.myfaces.trinidadinter
 
 import java.beans.Beans;
 
+
 import java.io.PrintWriter;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.Vector;
 import java.util.regex.Pattern;
 
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
@@ -53,13 +54,345 @@ import org.apache.myfaces.trinidadintern
 public class CSSGenerationUtils
 {
   /**
+   * Returns a LinkedHashMap of normalized propertyString to the StyleNodes that have them as properties.  A
+   * LinkedHashMap is returned to indicate that realative ordering is preserved between the first appearance of 
+   * selectors with unshared properties.
+   * However, a later style rule that shares its properties with a much earlier style rule will be defined with that
+   * style rule, thus having its definition moved up in the file.  Therefore, customers should rely on
+   * specificity rather than ordering to override styles.
+   * defined with 
+   * @param styleNodes
+   * @return
+   */
+  private static LinkedHashMap<String, List<StyleNode>> _buildMatchingStylesMap(List<StyleNode> styleNodes)
+  {
+    int styleCount = styleNodes.size();
+
+    // We track styles with matching properties in the following HashMap
+    // which maps property strings to List<StyleNode[]>.  We use a LinkedHashMap
+    LinkedHashMap<String, List<StyleNode>> matchingStylesMap = new LinkedHashMap<String, List<StyleNode>>(styleCount);
+    
+    // at this point the styles List<StyleNode> can contain both Styles with
+    // non-null selector or non-null name(aka alias). We only generate
+    // the styles where getSelector is non-null.
+    for (StyleNode styleNode : styleNodes)
+    {
+      if (styleNode.getSelector() != null)
+      {
+        // Get the property string (properties are sorted so that
+        // the order doesn't affect whether styles match).
+        String propertyString = _getSortedPropertyString(styleNode);
+
+        // we don't write out styles with no propertyStrings
+        if (!"".equals(propertyString))
+        {
+          // See if we already have a StyleNode with the same properties
+          List<StyleNode> matchingStyles = matchingStylesMap.get(propertyString);
+    
+          if (matchingStyles == null)
+          {    
+            // If we don't already have matching StyleNodes, create a new match list and cache it
+            matchingStyles = new ArrayList<StyleNode>(1);
+            matchingStylesMap.put(propertyString, matchingStyles);
+          }
+          
+          matchingStyles.add(styleNode);
+        }
+      }
+    }
+    
+    return matchingStylesMap;
+  }
+  
+  /**
+   * Writes the properties of a merged set of style rules
+   * @param out
+   * @param properties
+   * @param styleSheetName
+   * @param compressStyles
+   * @param baseURI
+   * @return
+   */
+  private static void _writeMergedProperties(
+    PrintWriter out, Iterable<PropertyNode> properties, String styleSheetName, boolean compressStyles, String baseURI)
+  {
+    out.print(" {");
+
+    boolean first = true;
+
+    for (PropertyNode property : properties)
+    {
+      String propName  = property.getName();
+      String propValue = property.getValue();
+
+      if ((propName != null) &&
+          (propValue != null) &&
+          (propValue.length() > 0 ))
+      {
+        // insert separator before all except the first property
+        if (first)
+          first = false;
+        else
+          out.print(';');
+
+        out.print(propName);
+        out.print(':');
+        String resolvedPropValue = CSSUtils.resolvePropertyValue(styleSheetName, baseURI, propName, propValue);
+        out.print(resolvedPropValue);
+      }
+    }
+
+    out.print('}'); 
+
+    // take out the newlines for performance
+    if (!compressStyles)
+    {
+      out.println();
+    }
+  }
+
+  /**
+   * Writes out a List of valid selectors
+   * @param out
+   * @param validSelectors
+   */
+  private static void _writeValidSelectors(PrintWriter out, List<String> validSelectors)
+  {
+    boolean first = true;
+
+    // Write out all of the style selectors for this property string
+    for (String validSelector : validSelectors)
+    {
+      // insert separator before all except the first property
+      if (first)
+        first = false;
+      else
+        out.print(',');
+
+      out.print(validSelector);
+    }
+  }
+
+  /**
+   * Given a List of mappedSelectors, returns a List of valid selectors to write
+   * @param compressStyles
+   * @param shortStyleClassMap
+   * @param namespacePrefixArray
+   * @param mappedSelectors
+   * @return
+   */
+  private static List<String>  _calculateValidSelectors(
+    boolean compressStyles, Map<String, String> shortStyleClassMap,
+    String[] namespacePrefixArray, List<String> mappedSelectors)
+  {
+     List<String> validSelectors = new ArrayList<String>(mappedSelectors.size() * 2);
+
+    // TODO: figure out why we write both the uncompressed & compressed styles for styles
+    // without a '|' character, shouldn't the uncompressed be enough on its own? This results
+    // in some ugly code here.
+      
+    // Write out all of the style selectors for this property string
+    for (String mappedSelector : mappedSelectors)
+    { 
+      String validFullNameSelector = null;
+
+      // write out the full selector if we aren't compressing styles or
+      // it doesn't have a '|' in the name which means it may be a user's public styleclass
+      // and we don't want to compress those; we will also write out the compressed
+      // version the public styleclasses in the next step.
+      if (!compressStyles || (mappedSelector.indexOf('|') == -1))
+      {
+        validFullNameSelector = getValidFullNameSelector(mappedSelector, namespacePrefixArray);
+
+        if (validFullNameSelector != null)
+        {
+          validSelectors.add(validFullNameSelector);
+        }
+      }
+
+      if (compressStyles)
+      {
+        String shortSelector = getShortSelector(shortStyleClassMap, namespacePrefixArray, mappedSelector);
+
+        // if the transformed full name is different than the shortSelector
+        // then write out the shortSelector, too.
+        if (shortSelector != null)
+        {
+          String validShortSelector = getValidFullNameSelector(shortSelector, namespacePrefixArray);
+
+          // if we wrote out a full style, check to see if we need to write out the short, too.
+          // if it is something different, write out the short, too.
+          if (validFullNameSelector != null)
+          {
+            //Since validFullNameSelector is not null, we know we wrote out a full style
+            // we write out a short style too in this case if it is different
+            // example: .PublicStyleClass is written out fully even in compressed mode, but
+            // it is different in compressed mode, so we write that out, too.
+            if (!validFullNameSelector.equals(validShortSelector))
+            {
+              validSelectors.add(validShortSelector);
+            }
+          }
+          else
+          {
+            validSelectors.add(validShortSelector);
+          }
+        }
+      }
+    }
+    
+    return validSelectors;
+  }
+
+  /**
+   * Writes the merged selectors represented by a mappedSelectors, returning how many selectors were written. If the number
+   * of selectors that would be written is greater to or equal than maxSelectors, nothing will be written and
+   * 0 will be returned.  It is up to the caller to retry with this entry later.
+   * @param out
+   * @param compressStyles
+   * @param shortStyleClassMap
+   * @param namespacePrefixArray
+   * @param maxSelectors
+   * @param mappedSelectors
+   * @return
+   */
+  private static int _writeMergedSelectors(
+    PrintWriter out, boolean compressStyles, Map<String, String> shortStyleClassMap,
+    String[] namespacePrefixArray, int maxSelectors, List<String> mappedSelectors)
+  {    
+    // to make this atomic, we first calculate all of the selectors that we will write out.  If we have space, we will
+    // then write the selectors out
+    List<String> validSelectors = _calculateValidSelectors(compressStyles, shortStyleClassMap, namespacePrefixArray,
+                                                           mappedSelectors);
+    
+    int selectorsToWrite = validSelectors.size();
+    
+    if (selectorsToWrite >= maxSelectors)
+    {
+      // not enough space, so abort
+      return 0;
+    }
+    else
+    {
+      _writeValidSelectors(out, validSelectors);
+    
+      return selectorsToWrite;
+    }
+  }
+
+  /**
+   * For a List of matching StyleNode, return the mapped version of their Selectors
+   */
+  private static List<String> _calculateMappedSelectors(
+    String[] namespacePrefixArray, Map<String, String> afSelectorMap, List<StyleNode> matchingStyleNodes)
+  {
+    List<String> mappedSelectors = new ArrayList<String>(matchingStyleNodes.size());
+
+    for (StyleNode matchingNode : matchingStyleNodes)
+    {
+      String matchingSelector = matchingNode.getSelector();
+
+      // We should always have a selector at this point
+      assert (matchingSelector != null);
+      
+      String mappedSelector = getMappedSelector(afSelectorMap, namespacePrefixArray, matchingSelector);
+
+      mappedSelectors.add(mappedSelector);
+    }
+    
+    return mappedSelectors;
+  }
+
+  /**
+   * Writes a merged style rule represented by a mergedEntry, returning how many selectors were written.  If the number
+   * of selectors that would be written is greater to or equal than maxSelectors, nothing will be written and
+   * 0 will be returned.  It is up to the caller to retry with this entry later.
+   * @param styleSheetName
+   * @param compressStyles
+   * @param shortStyleClassMap
+   * @param namespacePrefixArray
+   * @param afSelectorMap
+   * @param out
+   * @param maxSelectors
+   * @param mergedEntry
+   * @return
+   */
+  private static int _writeMergedEntry(
+    String                             styleSheetName,
+    String                             baseURI,
+    boolean                            compressStyles,
+    Map<String, String>                shortStyleClassMap,
+    String[]                           namespacePrefixArray,
+    Map<String, String>                afSelectorMap,
+    PrintWriter                        out,
+    int                                maxSelectors,
+    Map.Entry<String, List<StyleNode>> mergedEntry
+    )
+  {
+    String propertyString = mergedEntry.getKey();
+
+    // we shouldn't have any null or empty Strings
+    assert (propertyString != null);
+    assert (!"".equals(propertyString));
+
+    // Get all of the styles which share this property string.
+    List<StyleNode> matchingStyleNodes = mergedEntry.getValue();
+
+    // Actually, we should always have at least one StyleNode here
+    assert (!matchingStyleNodes.isEmpty());
+    
+    List<String> mappedSelectors = _calculateMappedSelectors(namespacePrefixArray, afSelectorMap, matchingStyleNodes);
+
+ 
+    // Write out all of the style selectors for this property string.  If 0 is returned, we've aborted
+    int numberSelectorsWritten = _writeMergedSelectors(out, compressStyles, shortStyleClassMap, namespacePrefixArray,
+                                                       maxSelectors, mappedSelectors);
+
+    // Now that we have written out the selectors, write out
+    // the properties
+    if (numberSelectorsWritten > 0)
+    {
+      // At this point, we could just write out the property string
+      // that we already created, but this string contains the properties
+      // in sorted order.  We prefer to attempt to preserve the order
+      // of the properties as specified in the XSS document.  So,
+      // we get the properties from the StyleNode object instead of
+      // using the propertyString
+      // because of this lameness, we use the order from the original StyleNode
+      Iterable<PropertyNode> properties = matchingStyleNodes.get(0).getProperties();
+  
+      _writeMergedProperties(out, properties, styleSheetName, compressStyles, baseURI);
+    }
+    
+    return numberSelectorsWritten;
+  }
+  
+    
+  /**
+   * Returns the maximum number of selectors per file that this agent supports
+   * @param context
+   * @return
+   */
+  private static int _getMaxSelectorsPerFile(StyleContext context)
+  {
+    if (TrinidadAgent.Application.IEXPLORER == context.getAgent().getAgentApplication())
+    {
+      return _MSIE_SELECTOR_LIMIT;
+    }
+    else
+    {
+      return Integer.MAX_VALUE;
+    }
+  }
+  
+  /**
    * Converts the specified set of StyleNodes to CSS. We output either full styleclass names or
    * compressed styleclass names.
    *
    * @param context The current StyleContext
    * @param styleSheetName The stylesheet name that is registered with the skin.
    *     e.g. skins/purple/purpleSkin.css
-   * @param styles The style nodes to convert
+   * @param styleNodes The style nodes to convert
    * @param writerFactory The factory to obtain {@link PrintWriter} instances
    * @param compressStyles This tells us whether or not we want to output the short names.
    * @param shortStyleClassMap A Map which maps style
@@ -72,20 +405,13 @@ public class CSSGenerationUtils
   public static void writeCSS(
     StyleContext        context,
     String              styleSheetName,
-    StyleNode[]         styles,
+    List<StyleNode>     styleNodes,
     StyleWriterFactory  writerFactory,
     boolean             compressStyles,
     Map<String, String> shortStyleClassMap,
     String[]            namespacePrefixArray,
-    Map<String, String> afSelectorMap
-    )
+    Map<String, String> afSelectorMap)
   {
-    PrintWriter out = writerFactory.createWriter();
-    if (out == null)
-    {
-      return;
-    }
-
     // writeCSS() attempts to produce a minimal set of style rules
     // by combining selectors with identical properties into a
     // single rule.  For example, we convert the following two
@@ -109,240 +435,88 @@ public class CSSGenerationUtils
     // During the second pass over the styles, we write out all matching
     // selectors for each style followed by the shared set of properties.
 
-    // We track styles with matching properties in the following HashMap
-    // which maps property strings to StyleNode[]s.
-    HashMap<String, StyleNode[]> matchingStylesMap =
-      new HashMap<String, StyleNode[]>(101);
-
-    // We also keep an array of the property strings that we generate
-    // during this pass, since we need these strings during the second
-    // pass to find matching StyleNodes.
-    String[] propertyStrings = new String[styles.length];
-
-    // at this point the styles StyleNode[] can contain both Styles with
-    // non-null selector or non-null name(aka alias). We only generate
-    // the styles where getSelector is non-null.
-    for (int i = 0; i < styles.length; i++)
-    {
-      StyleNode style = styles[i];
-
-      if (style.getSelector() != null)
-      {
-        // Get the property string (properties are sorted so that
-        // the order doesn't affect whether styles match).
-        String propertyString = _getSortedPropertyString(style);
-
-        // See if we already have a StyleNode with the same properties
-        StyleNode[] matchingStyles = matchingStylesMap.get(propertyString);
-
-        if (matchingStyles == null)
-        {
-          // If we don't already have matching StyleNodes, add this
-          // StyleNode to the map.
-          propertyStrings[i] = propertyString;
-          matchingStyles = new StyleNode[1];
-          matchingStyles[0] = style;
-        }
-        else
-        {
-          // If we already have matching StyleNodes, add this StyleNode
-          // to the end of the list of matching StyleNodes.
-          int length = matchingStyles.length;
-
-          StyleNode[] newMatchingStyles = new StyleNode[length + 1];
-          System.arraycopy(matchingStyles,
-                           0,
-                           newMatchingStyles,
-                           0,
-                           length);
-          newMatchingStyles[length] = style;
-          matchingStyles = newMatchingStyles;
-        }
-
-        // Rehash with the new value
-        matchingStylesMap.put(propertyString, matchingStyles);
-      }
-    }
-
-    // We'll start writing the CSS file now.  First
-    // write out the header with a time stamp
-    Date date = new Date();
-    if (!compressStyles)
-      out.println("/* This CSS file generated on " + date + " */");
-
-    // Keep track of the number of selectors written out. The reason? IE has a 4095 limit,
-    // and we want to warn when we get to that limit.
-    int numberSelectorsWritten = 0;
 
     // This is the second pass in which we write out the style rules
     // Get the baseURI up front so we don't have to recalculate it every time we find
     // a property value that contains url() and need to resolve the uri.
     String baseURI = CSSUtils.getBaseSkinStyleSheetURI(styleSheetName);
-    for (int i = 0; i < styles.length; i++)
-    {
-      StyleNode style = styles[i];
-      String propertyString = propertyStrings[i];
 
-      // We only write out styles for which we have a property string.
-      // All other entries correspond to styles which don't have selectors -
-      // or styles which will be rendered as a "matching" style.
-      if (propertyString != null && !(propertyString.equals("")))
-      {
-        // Get all of the styles which share this property string.
-        StyleNode[] matchingStyles = matchingStylesMap.get(propertyString);
-
-        // Actually, we should always have at least one StyleNode here
-        assert (matchingStyles != null);
-
-        // determine if the current CSS file can fit all of the CSS selectors, or if a new
-        // one will be needed.
-        // TODO: figure out why we write both the uncompressed & compressed styles for styles
-        // without a '|' character, shouldn't the uncompressed be enough on its own? This results
-        // in some ugly code here.
-        int stylesToBeWritten = 0;
-        String[] selectors = new String[matchingStyles.length];
-        String[] mappedSelectors = new String[matchingStyles.length];
+    LinkedHashMap<String, List<StyleNode>> matchingStylesMap = _buildMatchingStylesMap(styleNodes);
 
-        for (int j = 0; j < matchingStyles.length; j++)
-        {
-          selectors[j] = matchingStyles[j].getSelector();
-
-          // We should always have a selector at this point
-          assert (selectors[j] != null);
-
-          mappedSelectors[j] = getMappedSelector(afSelectorMap,
-                                                 namespacePrefixArray,
-                                                 selectors[j]);
+    Iterator<Map.Entry<String, List<StyleNode>>> mergedEntries = matchingStylesMap.entrySet().iterator();
+    
+    // Because of IE's selector limit, we need a slightly more complicated scheme for writing out the styles, so
+    // that we can create new CSS files as necessary
+    final int maxSelectorsPerFile = _getMaxSelectorsPerFile(context);
+        
+    // loop over all of the entries
+    while (mergedEntries.hasNext())
+    {
+      PrintWriter out = writerFactory.createWriter();
+      
+      if (out == null)
+      {
+        return;
+      }
+ 
+      if (!compressStyles)
+      {
+        Date date = new Date();
 
-          if (compressStyles && (mappedSelectors[j].indexOf('|') == -1))
-          {
-            stylesToBeWritten += 2;
-          }
-          else
-          {
-            stylesToBeWritten++;
-          }
-        }
+        // write out the header with a time stamp
+        out.println("/* This CSS file generated on " + date + " */");
+      }
 
-        if (numberSelectorsWritten + matchingStyles.length >= _MSIE_SELECTOR_LIMIT
-          && TrinidadAgent.Application.IEXPLORER == context.getAgent().getAgentApplication())
+      Map.Entry<String, List<StyleNode>> abortedEntry = null;
+      int fileSelectorsWritten = 0;
+      
+      // loop over all of the entries we can fit in a CSS file
+      while (abortedEntry == null && mergedEntries.hasNext())
+      {
+        // get the entry to write out.  This handles retrying any aborted entry
+        Map.Entry<String, List<StyleNode>> currEntry;
+        
+        if (abortedEntry != null)
         {
-          out.println("/* The number of CSS selectors in this file is " +
-                      numberSelectorsWritten + " */");
-          out = writerFactory.createWriter();
-          if (out == null)
-          {
-            return;
-          }
-          numberSelectorsWritten = 0;
+          currEntry = abortedEntry;
+          abortedEntry = null;
         }
-
-        // Write out all of the style selectors for this property string
-        for (int j = 0; j < matchingStyles.length; j++)
+        else
         {
-          String validFullNameSelector = null;
-
-          // write out the full selector if we aren't compressing styles or
-          // it doesn't have a '|' in the name which means it may be a user's public styleclass
-          // and we don't want to compress those; we will also write out the compressed
-          // version the public styleclasses in the next step.
-          if (!compressStyles || (mappedSelectors[j].indexOf('|') == -1))
-          {
-            validFullNameSelector =
-              getValidFullNameSelector(mappedSelectors[j], namespacePrefixArray);
-
-            if (validFullNameSelector != null)
-            {
-              out.print(validFullNameSelector);
-              numberSelectorsWritten++;
-            }
-          }
-
-
-
-          if (compressStyles)
-          {
-            String shortSelector = 
-              getShortSelector(shortStyleClassMap, namespacePrefixArray, mappedSelectors[j]);
-
-            // if the transformed full name is different than the shortSelector
-            // then write out the shortSelector, too.
-            if (shortSelector != null)
-            {
-              String validShortSelector =
-                getValidFullNameSelector(shortSelector, namespacePrefixArray);
-
-              // if we wrote out a full style, check to see if we need to write out the short, too.
-              // if it is something different, write out the short, too.
-              if (validFullNameSelector != null)
-              {
-                //Since validFullNameSelector is not null, we know we wrote out a full style
-                // we write out a short style too in this case if it is different
-                // example: .PublicStyleClass is written out fully even in compressed mode, but
-                // it is different in compressed mode, so we write that out, too.
-                if (!validFullNameSelector.equals(validShortSelector))
-                {
-                  out.print(',');
-                  out.print(validShortSelector);
-                  numberSelectorsWritten++;
-                }
-              }
-              else
-              {
-                out.print(validShortSelector);
-                numberSelectorsWritten++;
-              }
-            }
-          }
-
-          // Write out a separator between matching selectors
-          if (j < (matchingStyles.length - 1))
-            out.print(",");
+          currEntry = mergedEntries.next();
         }
 
-        // Now that we have written out the selectors, write out
-        // the properties
-        out.print(" {");
-
-        // At this point, we could just write out the property string
-        // that we already created, but this string contains the properties
-        // in sorted order.  We prefer to attempt to preserve the order
-        // of the properties as specified in the XSS document.  So,
-        // we get the properties from the StyleNode object instead of
-        // using the propertyString
-        Iterable<PropertyNode> properties = style.getProperties();
-        boolean first = true;
-
-        for (PropertyNode property : properties)
+        // write the entry
+        int selectorsLeft = maxSelectorsPerFile - fileSelectorsWritten;
+        int entrySelectorsWritten = _writeMergedEntry(styleSheetName, baseURI, compressStyles, shortStyleClassMap,
+                                                      namespacePrefixArray, afSelectorMap, out, selectorsLeft,
+                                                      currEntry);
+        // detect abort
+        if (entrySelectorsWritten == 0)
         {
-          String propName = property.getName();
-          String propValue = property.getValue();
-
-          if ((propName != null) &&
-              (propValue != null) &&
-              (propValue.length() > 0 ))
-          {
-            if (!first)
-              out.print(";");
-            else
-              first = false;
-
-            out.print(propName);
-            out.print(":");
-            String resolvedPropValue =
-              CSSUtils.resolvePropertyValue(styleSheetName, baseURI, propName, propValue);
-            out.print(resolvedPropValue);
-
-          }
+          abortedEntry = currEntry;
         }
-
-        if (compressStyles)
-          out.print("}"); // take out the newlines for performance
         else
-          out.println("}");
+        {
+          fileSelectorsWritten += entrySelectorsWritten;
+        }
+      }
+      
+      if (!compressStyles)
+      {
+        out.print("/* The number of CSS selectors in this file is ");
+        out.print(fileSelectorsWritten);
+        out.println(" */");
+      }
+      
+      if (out.checkError())
+      {
+        _LOG.severe("Error writing stylehseet:" + styleSheetName);
       }
+      
+      // close this PrintWriter
+      out.close();
     }
-    out.println("/* The number of CSS selectors in this file is " + numberSelectorsWritten + " */");
   }
 
   /**
@@ -380,12 +554,10 @@ public class CSSGenerationUtils
     {
       String[] shortSelectorArray  = StyleUtils.splitStringByWhitespace(shortSelector);
 
-      shortSelector =
-        _getMappedNSSelector(shortStyleClassMap,
-                             namespacePrefixArray,
-                             shortSelector,
-                             shortSelectorArray,
-                             true);
+      shortSelector = _getMappedNSSelector(shortStyleClassMap,
+                                           namespacePrefixArray,
+                                           shortSelectorArray,
+                                           true);
     }
     return shortSelector;
   }
@@ -810,7 +982,6 @@ public class CSSGenerationUtils
       // untouched. e.g., .AFInstructionText maps to .AFInstructionText
       mappedSelector = _getMappedNSSelector(afSelectorMap,
                                             namespacePrefixArray,
-                                            selector,
                                             selectorArray,
                                             false);
     }
@@ -872,9 +1043,6 @@ public class CSSGenerationUtils
    * @param map         if shortenPass is true, then this map shortens the
    *                    af| selector. else, it maps the public af| selector
    *                    to the internal selector.
-   * @param namespace   most likely, "af|". The selectors with this namespace
-   *                    are the ones we map.
-   * @param selector    selector to map.
    * @param selectorArray selectorArray is the selector split into pieces based on the ' '
    * @param shorten     if true, then we'll add the "." to the mapped selector.
    * @return            the selector, mapped.
@@ -882,7 +1050,6 @@ public class CSSGenerationUtils
   private static String _getMappedNSSelector (
     Map<String, String> map,
     String[]            nsPrefixArray,
-    String              selector,
     String[]            selectorArray,
     boolean             shorten)
   {
@@ -1146,30 +1313,11 @@ public class CSSGenerationUtils
     return (Character.isWhitespace(c) || (c == ':') || (c == '.') || (c == '['));
   }
 
-  // Gets the properties of the specified StyleNode in sorted
-  // order as a String.
-  private static String _getSortedPropertyString(StyleNode style)
-  {
-    // First, pull the properties out of the StyleNode
-    // -= Simon Lessard =-
-    // TODO: Check if synchronization is needed, otherwise uses
-    //       an ArrayList instead. Even if synchronization is needed
-    //       Collections.synchronizedList(ArrayList) would probably be
-    //       a better choice.
-    Vector<PropertyNode> v = new Vector<PropertyNode>();
-    Iterable<PropertyNode> propertyNodeList = style.getProperties();
-    for (PropertyNode propertyNode : propertyNodeList)
-      v.addElement(propertyNode);
-
-    PropertyNode[] properties = new PropertyNode[v.size()];
-    v.copyInto(properties);
-
-    // Sort the properties so that the order of the properties won't
-    // come into play when comparing property strings.
-    Arrays.sort(properties,PropertyNodeComparator.sharedInstance());
-
-    // Compute the StringBuffer size
-    int bufferSize = 0;
+  private static int _computeBuilderSize(PropertyNode[] properties)
+  {
+    // Compute the StringBuilder size
+    int builderSize = 0;
+    
     for (int i = 0; i < properties.length; i++)
     {
       PropertyNode property = properties[i];
@@ -1178,37 +1326,68 @@ public class CSSGenerationUtils
 
       if ((name != null) && (value != null))
       {
-        bufferSize += property.getName().length();
-        bufferSize += property.getValue().length();
+        builderSize += property.getName().length();
+        builderSize += property.getValue().length();
 
         // Leave room for a separator
-        bufferSize++;
+        builderSize++;
       }
     }
+    
+    return builderSize;
+  }
 
-    StringBuffer buffer = new StringBuffer(bufferSize);
+  private static String _getSortedPropertyString(PropertyNode[] sortedProperties)
+  {
+    int builderSize = _computeBuilderSize(sortedProperties);
+    
+    StringBuilder builder = new StringBuilder(builderSize);
     boolean first = true;
 
-    for (int i = 0; i < properties.length; i++)
+    for (int i = 0; i < sortedProperties.length; i++)
     {
-      PropertyNode property = properties[i];
-      String name = property.getName();
-      String value = property.getValue();
+      PropertyNode property = sortedProperties[i];
+      String       name     = property.getName();
+      String       value    = property.getValue();
 
       if ((name != null) && (value != null))
       {
         if (!first)
-          buffer.append(";");
+          builder.append(';');
         else
           first = false;
 
-        buffer.append(name);
-        buffer.append(":");
-        buffer.append(value);
+        builder.append(name);
+        builder.append(':');
+        builder.append(value);
       }
     }
 
-    return buffer.toString();
+    return builder.toString();
+  }
+  
+  /**
+   * Returns the properties of the specified StyleNode in property name sorted order as a String.
+   * If the Stylenode contains no properties, the empty String is returned
+   */
+  private static String _getSortedPropertyString(StyleNode styleNode)
+  {
+    Collection<PropertyNode> nodeProperties = styleNode.getProperties();
+    int propertyCount = nodeProperties.size();
+      
+    if (propertyCount == 0)
+      return "";
+    
+    // =-= bts the extra step of copying the PropertyNodes into the array is kind of lame.  I'm wondering
+    // if it would be better for the StyleNodes to sort the properties for us
+    List<PropertyNode> properties       = new ArrayList<PropertyNode>(nodeProperties);
+    PropertyNode[]     sortedProperties = properties.toArray(new PropertyNode[propertyCount]);
+
+    // Sort the properties so that the order of the properties won't
+    // come into play when comparing property strings.
+    Arrays.sort(sortedProperties, PropertyNodeNameComparator.sharedInstance());
+
+    return _getSortedPropertyString(sortedProperties);
   }
 
   /**
@@ -1305,13 +1484,14 @@ public class CSSGenerationUtils
 
 
   // Comparator that sorts PropertyNodes by name
-  private static class PropertyNodeComparator implements Comparator<PropertyNode>
+  private static class PropertyNodeNameComparator implements Comparator<PropertyNode>
   {
     public static Comparator<PropertyNode> sharedInstance()
     {
-      return _sInstance;
+      return _INSTANCE;
     }
 
+    @Override
     public int compare(PropertyNode o1, PropertyNode o2)
     {
       String name1 = (o1 == null) ? null : o1.getName();
@@ -1329,11 +1509,17 @@ public class CSSGenerationUtils
 
       return name1.compareTo(name2);
     }
+    
+    @Override
+    public boolean equals(Object o)
+    {
+      // we only have our singleton instance
+      return (this == o);
+    }
 
-    private PropertyNodeComparator() {}
+    private PropertyNodeNameComparator() {}
 
-    private static final Comparator<PropertyNode> _sInstance =
-      new PropertyNodeComparator();
+    private static final Comparator<PropertyNode> _INSTANCE = new PropertyNodeNameComparator();
   }
 
   /**

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/NameUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/NameUtils.java?rev=1503980&r1=1503979&r2=1503980&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/NameUtils.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/NameUtils.java Wed Jul 17 03:00:30 2013
@@ -18,10 +18,9 @@
  */
 package org.apache.myfaces.trinidadinternal.style.util;
 
-import java.util.Iterator;
+import java.util.Collection;
 import java.util.Locale;
 import java.util.Set;
-import java.util.Vector;
 
 import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent;
 import org.apache.myfaces.trinidadinternal.skin.AgentAtRuleMatcher;
@@ -29,6 +28,7 @@ import org.apache.myfaces.trinidadintern
 import org.apache.myfaces.trinidadinternal.style.xml.XMLConstants;
 import org.apache.myfaces.trinidadinternal.style.xml.parse.StyleSheetDocument;
 import org.apache.myfaces.trinidadinternal.style.xml.parse.StyleSheetNode;
+
 import org.apache.myfaces.trinidadinternal.util.nls.LocaleUtils;
 
 /**
@@ -36,9 +36,9 @@ import org.apache.myfaces.trinidadintern
  * 
  * @version $Name: $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/style/util/NameUtils.java#0 $) $Date: 10-nov-2005.18:58:52 $
  */
-public class NameUtils
+public final class NameUtils
 {
-  private NameUtils() {}
+  private NameUtils(){}
 
   /**
    * Returns the id of the browser with the specified name
@@ -271,15 +271,8 @@ public class NameUtils
   public static String getContextName(StyleContext context, StyleSheetDocument document)
   {
     // Copy the matching style sheets into an array
-    Iterator<StyleSheetNode> e = document.getStyleSheets(context);
-    // -= Simon Lessard =-
-    // TODO: Check if synchronization is truly required.
-    Vector<StyleSheetNode> v = new Vector<StyleSheetNode>();
-    while (e.hasNext())
-      v.addElement(e.next());
-    StyleSheetNode[] styleSheets = new StyleSheetNode[v.size()];
-    v.copyInto(styleSheets);
-
+    Collection<StyleSheetNode> styleSheets = document.getStyleSheetsAsCollection(context);
+        
     // Determine which variants actually match the variants specified
     // in the matching style sheets
     int localeMatch = _isLocaleMatch(context, styleSheets);
@@ -493,10 +486,7 @@ public class NameUtils
   // locale variant of any matching style sheet. Returns an integer
   // indicating whether the language matches, the language and
   // country matches, or no match
-  private static int _isLocaleMatch(
-      StyleContext context,
-      StyleSheetNode[] styleSheets
-      )
+  private static int _isLocaleMatch(StyleContext context, Collection<StyleSheetNode> styleSheets)
   {
     Locale locale = context.getLocaleContext().getTranslationLocale();
     if (locale == null)
@@ -509,9 +499,9 @@ public class NameUtils
 
     boolean languageMatch = false;
 
-    for (int i = 0; i < styleSheets.length; i++)
+    for (StyleSheetNode styleSheet : styleSheets)
     {
-      Iterable<Locale> localeList = styleSheets[i].getLocales();
+      Iterable<Locale> localeList = styleSheet.getLocales();
       for (Locale tmpLocale : localeList)
       {
         if (language.equals(tmpLocale.getLanguage()))
@@ -542,13 +532,12 @@ public class NameUtils
 
   // Tests whether the direction specified in the context matches
   // the direction variant of any matching style sheet
-  private static boolean _isDirectionMatch(StyleSheetNode[] styleSheets)
+  private static boolean _isDirectionMatch(Collection<StyleSheetNode> styleSheets)
   {
     // If any style sheet has a non-null direction variant, we must
     // have a direction match.
-    for (int i = 0; i < styleSheets.length; i++)
+    for (StyleSheetNode styleSheet : styleSheets)
     {
-      StyleSheetNode styleSheet = styleSheets[i];
       if (styleSheet.getReadingDirection() != LocaleUtils.DIRECTION_DEFAULT)
         return true;
     }
@@ -556,14 +545,14 @@ public class NameUtils
     return false;
   }
   
-  private static boolean _isModeMatch(StyleSheetNode[]styleSheets)
+  private static boolean _isModeMatch(Collection<StyleSheetNode> styleSheets)
   {
-    for (int i = 0; i < styleSheets.length; i++)
+    for (StyleSheetNode styleSheet : styleSheets)
     {
-      StyleSheetNode styleSheet = styleSheets[i];
-      if(styleSheet.getMode()!=ModeUtils.MODE_DEFAULT)
+      if(styleSheet.getMode() != ModeUtils.MODE_DEFAULT)
         return true;
     }
+    
     return false;
   }
 
@@ -573,8 +562,7 @@ public class NameUtils
    * @return a boolean array with the first value being the browser match
    *          and the second value being the version match
    */
-  private static boolean[] _isBrowserAndVersionMatch(StyleContext context,
-      StyleSheetNode[] styleSheets)
+  private static boolean[] _isBrowserAndVersionMatch(StyleContext context, Collection<StyleSheetNode> styleSheets)
   {
     TrinidadAgent agent = context.getAgent();
     TrinidadAgent.Application browser = agent.getAgentApplication();
@@ -587,9 +575,9 @@ public class NameUtils
     
     // If any style sheet has a non-null browser variant, we must
     // have a browser match.
-    for (int i = 0; i < styleSheets.length; i++)
+    for (StyleSheetNode styleSheet : styleSheets)
     {
-      AgentAtRuleMatcher agentMatcher = styleSheets[i].getAgentMatcher();
+      AgentAtRuleMatcher agentMatcher = styleSheet.getAgentMatcher();
       
       if (agentMatcher != null)
       {
@@ -617,8 +605,7 @@ public class NameUtils
 
   // Tests whether the platform specified in the context matches
   // the platform variant of any matching style sheet
-  private static boolean _isPlatformMatch(StyleContext context,
-      StyleSheetNode[] styleSheets)
+  private static boolean _isPlatformMatch(StyleContext context, Collection<StyleSheetNode> styleSheets)
   {
     int platform = context.getAgent().getAgentOS();
     if (platform == TrinidadAgent.OS_UNKNOWN)
@@ -626,9 +613,9 @@ public class NameUtils
 
     // If any style sheet has a non-null platform variant, we must
     // have a platform match.
-    for (int i = 0; i < styleSheets.length; i++)
+    for (StyleSheetNode styleSheet : styleSheets)
     {
-      if (!(styleSheets[i].getPlatforms().isEmpty()))
+      if (!(styleSheet.getPlatforms().isEmpty()))
         return true;
     }
 
@@ -638,18 +625,16 @@ public class NameUtils
   // Tests whether the high contrast value specified in the context matches
   // the high contrast value of any matching style sheet
   private static boolean _isHighContrastMatch(
-      StyleContext context,
-      StyleSheetNode[] styleSheets)
+      StyleContext context, Collection<StyleSheetNode> styleSheets)
   {
     if (!context.getAccessibilityProfile().isHighContrast())
       return false;
 
     // If the high-contrast accessibility profile property is specified
     // on any style sheet, we have a match.
-    for (int i = 0; i < styleSheets.length; i++)
+    for (StyleSheetNode styleSheet : styleSheets)
     {
-      if (styleSheets[i].getAccessibilityProperties().contains(
-            XMLConstants.ACC_HIGH_CONTRAST))
+      if (styleSheet.getAccessibilityProperties().contains(XMLConstants.ACC_HIGH_CONTRAST))
         return true;
     }
 
@@ -659,18 +644,16 @@ public class NameUtils
   // Tests whether the high contrast value specified in the context matches
   // the high contrast value of any matching style sheet
   private static boolean _isLargeFontsMatch(
-      StyleContext context,
-      StyleSheetNode[] styleSheets)
+      StyleContext context, Collection<StyleSheetNode> styleSheets)
   {
     if (!context.getAccessibilityProfile().isLargeFonts())
       return false;
 
     // If the large-fonts accessibility profile property is specified
     // on any style sheet, we have a match.
-    for (int i = 0; i < styleSheets.length; i++)
+    for (StyleSheetNode styleSheet : styleSheets)
     {
-      if (styleSheets[i].getAccessibilityProperties().contains(
-            XMLConstants.ACC_LARGE_FONTS))
+      if (styleSheet.getAccessibilityProperties().contains(XMLConstants.ACC_LARGE_FONTS))
         return true;
     }
 

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/StyleWriterFactory.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/StyleWriterFactory.java?rev=1503980&r1=1503979&r2=1503980&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/StyleWriterFactory.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/StyleWriterFactory.java Wed Jul 17 03:00:30 2013
@@ -29,9 +29,8 @@ import java.io.PrintWriter;
 public interface StyleWriterFactory
 {
   /**
-   * Create a print writer to write to a new file. The previous writer, if it exists, should
-   * be closed by this method call
-   * @return a new instance of a print writer
+   * Creates a new PrintWriter.  The caller is responsible for closing the Writer when done with it
+   * @return a new instance of a PrintWriter
    */
   PrintWriter createWriter();
 }

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java?rev=1503980&r1=1503979&r2=1503980&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java Wed Jul 17 03:00:30 2013
@@ -20,11 +20,13 @@ package org.apache.myfaces.trinidadinter
 
 import java.awt.Color;
 
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -32,7 +34,6 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.Stack;
 
 import java.util.regex.Pattern;
 
@@ -219,10 +220,8 @@ public class StyleSheetDocument
     // We also need to provide a Map for storing selector-based
     // styles and another for storing name-based styles, used by
     // _resolveStyle() to store results
-    HashMap<String, StyleNode> resolvedStyles = 
-      new HashMap<String, StyleNode>();
-    HashMap<String, StyleNode> resolvedNamedStyles = 
-      new HashMap<String, StyleNode>();
+    Map<String, StyleNode> resolvedStyles      = new HashMap<String, StyleNode>();
+    Map<String, StyleNode> resolvedNamedStyles = new HashMap<String, StyleNode>();
 
     // Keep track of all selectors and names that we've already
     // found, so that we don't bother re-resolving them
@@ -232,8 +231,8 @@ public class StyleSheetDocument
     // not yet have been encountered by this top loop.  We
     // could eliminate the need for this if we were willing
     // to give up the ordering of the list
-    Set<String> foundNames = new HashSet<String>();
-    List<IconNode> iconNodes = new ArrayList<IconNode>();
+    Set<String>    foundNames = new HashSet<String>();
+    List<IconNode> iconNodes  = new ArrayList<IconNode>();
 
     // We want to cache IDs in this use of StyleSheetList. We'll use the cache
     // when we resolve styles.
@@ -246,44 +245,40 @@ public class StyleSheetDocument
       // iterate through each one and add to the list.
       for (IconNode iconNodeFromStyleSheet : iconNodeList)
       {
-            String id;
-            boolean isFound;
+        String id;
+        boolean isFound;
 
 
-            id = iconNodeFromStyleSheet.getIconName();
-            isFound = foundNames.contains(id);
-            
+        id = iconNodeFromStyleSheet.getIconName();
+        isFound = foundNames.contains(id);
+        
 
-            // If we've already seen that node/selector, no need to look
-            // for it again
-            if (!isFound)
-            {
-              StyleNode resolvedNode = _resolveStyleNode(context, true,
-                                                         styleSheets,
-                                                         resolvedStyles,
-                                                         resolvedNamedStyles,
-                                                         null,
-                                                         null,
-                                                         id,
-                                                         false);
+        // If we've already seen that node/selector, no need to look
+        // for it again
+        if (!isFound)
+        {
+          StyleNode resolvedNode = _resolveStyleNode(context, true,
+                                                     styleSheets,
+                                                     resolvedStyles,
+                                                     resolvedNamedStyles,
+                                                     null,
+                                                     null,
+                                                     id,
+                                                     false);
 
-              // Create the Icon
-              
-              if (resolvedNode != null)        
-              {
- 
-                Icon icon = _createIconFromNode(resolvedNode);
- 
-                if (icon != null)
-                {
-                  iconNodes.add(new IconNode(id, icon, resolvedNode));
+          // Create the Icon
+          if (resolvedNode != null)        
+          {
 
-                  foundNames.add(id);
-                }
+            Icon icon = _createIconFromNode(resolvedNode);
 
-              }
-       
+            if (icon != null)
+            {
+              iconNodes.add(new IconNode(id, icon, resolvedNode));
 
+              foundNames.add(id);
+            }
+          }
         }
       }
     }
@@ -465,7 +460,7 @@ public class StyleSheetDocument
     // could eliminate the need for this if we were willing
     // to give up the ordering of the list
     Set<String> foundSelectors = new HashSet<String>();
-    Set<String> foundNames = new HashSet<String>();
+    Set<String> foundNames     = new HashSet<String>();
 
     // Now, loop through all StyleNodes in all StyleSheetNodes
     
@@ -647,8 +642,8 @@ public class StyleSheetDocument
     StyleSheetList         styleSheets,
     Map<String, StyleNode> resolvedStyles,
     Map<String, StyleNode> resolvedNamedStyles,
-    Stack<String>          includesStack,
-    Stack<String>          namedIncludesStack,
+    Deque<String>          includesStack,
+    Deque<String>          namedIncludesStack,
     String                 id,
     boolean                isNamed
     )
@@ -704,19 +699,15 @@ public class StyleSheetDocument
     // Push this style onto the appropriate include stack
     if (isNamed)
     {
-      // -= Simon Lessard =- 
-      // TODO: Check if synchronization is truly required
       if (namedIncludesStack == null)
-        namedIncludesStack = new Stack<String>();
+        namedIncludesStack = new ArrayDeque<String>();
 
       namedIncludesStack.push(id);
     }
     else
     {
-      // -= Simon Lessard =- 
-      // TODO: Check if synchronization is truly required
       if (includesStack == null)
-        includesStack = new Stack<String>();
+        includesStack = new ArrayDeque<String>();
 
       includesStack.push(id);
     }
@@ -726,6 +717,7 @@ public class StyleSheetDocument
     // .someStyle {color: red} .someStyle {font-size: 11px}
     // you will get two StyleNodes, and the properties will get merged together.
     List<StyleNode> nodeList = styleSheets.styleNodes(id, isNamed);
+    
     // get the StyleNodes from each iconNodeList and add it to the StyleNode list
     if (forIconNode)
     {
@@ -765,7 +757,11 @@ public class StyleSheetDocument
       includesStack.pop();
     }
 
-    StyleNode resolvedNode = entry.toStyleNode();
+    // Set<String> blackList = _DESTYLED_SUPPRESSED_PROPERTIES;
+    //Set<String> blackList = _IE_SUPPRESSED_PROPERTIES;
+    Set<String> blackList = null;
+      
+    StyleNode resolvedNode = entry.toStyleNode(blackList);
 
     // If we got a node, add it in to our list
     if (resolvedNode != null)
@@ -803,8 +799,8 @@ public class StyleSheetDocument
     StyleSheetList         styleSheets,
     Map<String, StyleNode> resolvedStyles,
     Map<String, StyleNode> resolvedNamedStyles,
-    Stack<String>          includesStack,
-    Stack<String>          namedIncludesStack,
+    Deque<String>          includesStack,
+    Deque<String>          namedIncludesStack,
     StyleEntry             entry,
     List<StyleNode>        nodeList)
   {
@@ -1037,8 +1033,8 @@ public class StyleSheetDocument
     StyleSheetList         styleSheets,
     Map<String, StyleNode> resolvedStyles,
     Map<String, StyleNode> resolvedNamedStyles,
-    Stack<String>          includesStack, 
-    Stack<String>          namedIncludesStack,
+    Deque<String>          includesStack, 
+    Deque<String>          namedIncludesStack,
     IncludePropertyNode    includeProperty)
   {
     String includeID = null;
@@ -1078,14 +1074,17 @@ public class StyleSheetDocument
     Iterable<PropertyNode> properties = node.getProperties();
     for (PropertyNode propertyNode : properties)
     {
-      entry.addIncludedProperty(propertyNode);
+      if (propertyNode != null)
+      {
+        entry.addProperty(propertyNode);
+      }
     }
     
     // server-side skin properties
     Iterable<PropertyNode> skinProperties = node.getSkinProperties();
     for (PropertyNode skinPropertyNode : skinProperties)
     {
-      entry.addIncludedSkinProperty(skinPropertyNode);
+      entry.addSkinProperty(skinPropertyNode);
     }
   }
 
@@ -1119,7 +1118,7 @@ public class StyleSheetDocument
           _getIncludedProperties(propertyName, localPropertyName, properties);
         for (PropertyNode propertyToAdd : includedProperties)
         {
-          entry.addIncludedSkinProperty(propertyToAdd);
+          entry.addSkinProperty(propertyToAdd);
         }
         
         // most likely this block of code will run instead of the above code
@@ -1131,7 +1130,7 @@ public class StyleSheetDocument
           _getIncludedProperties(propertyName, localPropertyName, skinProperties);
         for (PropertyNode propertyToAdd : includedSkinProperties)
         {
-          entry.addIncludedSkinProperty(propertyToAdd);
+          entry.addSkinProperty(propertyToAdd);
         }
        
       }
@@ -1144,7 +1143,7 @@ public class StyleSheetDocument
           _getIncludedProperties(propertyName, localPropertyName, properties);
         for (PropertyNode propertyToAdd : includedProperties)
         {
-          entry.addIncludedProperty(propertyToAdd);
+          entry.addProperty(propertyToAdd);
         }
       }
 
@@ -1229,23 +1228,7 @@ public class StyleSheetDocument
     }
     return propertyNodesToAdd;
   }
-
-  // Returns a count of the non-null items in the List
-  private static int _getNonNullCount(List<?> list)
-  {
-    if (list == null)
-      return 0;
-
-    int count = 0;
-    for (int i = 0; i < list.size(); i++)
-    {
-      if (list.get(i) != null)
-        count++;
-    }
-
-    return count;
-  }
-
+  
   // Static utility method used by StyleEntry & FontSizeConverter
   // to get the real font size, taking relative size into account
   static PropertyNode _getRealFontSize(
@@ -1356,7 +1339,7 @@ public class StyleSheetDocument
   }
   
   // Tests whether the value is present in the (possibly null) stack.
-  private static boolean _stackContains(Stack<?> stack, Object value)
+  private static boolean _stackContains(Deque<?> stack, Object value)
   {
     if (stack == null)
       return false;
@@ -1636,6 +1619,25 @@ public class StyleSheetDocument
 
       return 1;
     }
+    
+    @Override
+    public boolean equals(Object o)
+    {
+      if (this == o)
+        return true;
+      
+      if (!(o instanceof StyleSheetComparator))
+        return false;
+      
+      StyleSheetComparator other = (StyleSheetComparator)o;
+      
+      return _direction == other._direction        &&
+             _mode      == other._mode             &&
+             _locale.equals(other._locale)         &&
+             _accProfile.equals(other._accProfile) &&
+             _agent.equals(other._agent)           &&
+             _styleSheets.equals(other._styleSheets);
+    }
 
     private int _compareOrder(Object item1, Object item2)
     {
@@ -1677,16 +1679,6 @@ public class StyleSheetDocument
     // The name of the style
     public final String name;
 
-    // Empty private constructor - this exists only to prevent
-    // the compiler from complaining about our blank final vars
-    // It should never be called.
-    private StyleEntry()
-    {
-      assert false;
-      selector = null;
-      name = null;
-    }
-
     // Create a StyleEntry with the specified selector, name
     public StyleEntry(String selector, String name)
     {
@@ -1765,84 +1757,65 @@ public class StyleSheetDocument
       _propertyCount = 0;
       _relativeFontSize = 0;
     }
-
-    // Returns the count of properties defined by this style
-    public int getPropertyCount()
-    {
-      return _propertyCount;
-    }
-
-    // Returns an Iterator of the properties defined by this style
-    public Iterator<PropertyNode> getProperties()
-    {
-      if (_properties == null)
-        return null;
-
-      return new
-        FontSizeConverter(
-          new NonNullIterator<PropertyNode>(
-            _properties.iterator()),
-          _relativeFontSize);
-    }
-
-    // Adds an "included" property.  Include propreties are
-    // properties which are indirectly included in the style
-    // via an <includeStyle> element.
-    // TODO jmw Since we no longer track included properties separately, we should delete.
-    public void addIncludedProperty(PropertyNode property)
-    {
-      // We no longer track included properties separately.
-      // The reason for this is that it should be possible
-      // to override a <property> value using an included
-      // property in a custom style sheet.  (See bug 2896824.)
-      // So, we just add included properties to the main
-      // property list.
-      addProperty(property);
-    }
     
-    public void addIncludedSkinProperty(PropertyNode skinProperty)
-    {
-      addSkinProperty(skinProperty);
-    }
-
-    // Converts this StyleEntry to a StyleNode
-    public StyleNode toStyleNode()
+    /**
+     * Returns the filtered and trimmed PropertyNode[] for the properties
+     * @param unfilteredProperties
+     * @param blackList
+     * @return
+     */
+    private static PropertyNode[] _filterAndTrimPropertyNodes(
+      List<PropertyNode> unfilteredProperties, Set<String> blackList, int relativeFontSize)
     {
-      // Create an PropertyNode array to pass to the new StyleNode.
-      // The PropertyNode array includes both the included properties
-      // and the properties defined directly within this style.
-      int propertyCount = _getNonNullCount(_properties);
-      int skinPropertyCount = _getNonNullCount(_skinProperties);
-                                                          
-
-      if (propertyCount + skinPropertyCount == 0)
-        return null;
-
-      PropertyNode[] properties = new PropertyNode[propertyCount];
-      PropertyNode[] skinProperties = new PropertyNode[skinPropertyCount];
-
-      // Copy in the properties
-      _nonNullCopyInto(_properties, properties, 0);
-      _nonNullCopyInto(_skinProperties, skinProperties, 0);
-
-      // Adjust the font size based for relative font
-      if (_relativeFontSize != 0)
+      int propertyCount = (unfilteredProperties != null) ?  unfilteredProperties.size() : 0;
+      
+      if (propertyCount == 0)
       {
-        for (int i = 0; i < properties.length; i++)
+        return _EMPTY_PROPERTY_NODE_ARRAY;
+      }
+      else
+      { 
+        // filter the properties
+        PropertyNode[] filteredProperties = _filteredCopyInto(unfilteredProperties, blackList,
+                                                              new PropertyNode[propertyCount]);
+        
+        // Adjust the font size based for relative font
+        if (relativeFontSize != 0)
         {
-          PropertyNode property = properties[i];
-          if (_FONT_SIZE_NAME.equals(property.getName()))
+          for (int i = 0; i < filteredProperties.length; i++)
           {
-            properties[i] = _getRealFontSize(property, _relativeFontSize);
-            break;
+            PropertyNode property = filteredProperties[i];
+            
+            if (_FONT_SIZE_NAME.equals(property.getName()))
+            {
+              filteredProperties[i] = _getRealFontSize(property, relativeFontSize);
+              break;
+            }
           }
         }
+        
+        return filteredProperties;
       }
+    }
 
-      // Create and return our StyleNode.  We don't need to specify
-      // a name or included styles, as they have already been resolved.
-      // StyleNode(name, selector, properties, skinProperties, includedStyles, includedProperties, inhibitedProperties)
-      return new StyleNode(name, selector, properties, skinProperties, null, null,null, null);
+    // Converts this StyleEntry to a StyleNode
+    public StyleNode toStyleNode(Set<String> blackList)
+    {
+      PropertyNode[] properties     = _filterAndTrimPropertyNodes(_properties, blackList, _relativeFontSize);
+      PropertyNode[] skinProperties = _filterAndTrimPropertyNodes(_skinProperties, null, 0);
+                                                      
+      if (properties.length + skinProperties.length == 0)
+      {
+        // no properties, so nothing to return
+        return null;
+      }
+      else
+      {
+        // Create and return our StyleNode.  We don't need to specify
+        // a name or included styles, as they have already been resolved.
+        // StyleNode(name, selector, properties, skinProperties, includedStyles, includedProperties, inhibitedProperties)
+        return new StyleNode(name, selector, properties, skinProperties, null, null,null, null);
+      }
     }
 
     // Removes the PropertyNode with the specified name from the
@@ -1906,23 +1879,48 @@ public class StyleSheetDocument
 
       return false;
     }
-
-    // Copies the non-null entries from the source vector to the
-    // target Object array, starting at the specified index
-    private void _nonNullCopyInto(
-        ArrayList<? extends Object> source, 
-        Object[] target, 
-        int start)
+    
+    /**
+     * For each non-null PropertyNode in the source List, filter out all null items and any ProeprtyNode's whose
+     * name appears in the blacklist
+     * 
+     * The returned Array might not be the initially passed array if filtering changed the number of elements
+     */
+    private static PropertyNode[] _filteredCopyInto(
+      List<? extends PropertyNode> source,
+      Set<String> blackList,
+      PropertyNode[] target)
     {
-      if (source == null)
-        return;
-
-      for (int i = 0; i < source.size(); i++)
+      int copyIndex = 0;
+      
+      int sourceSize = source.size();
+      
+      for (int i = 0; i < sourceSize; i++)
       {
-        Object o = source.get(i);
-
-        if (o != null)
-          target[start++] = o;
+        PropertyNode property = source.get(i);
+    
+        if ((property != null) && ((blackList == null) || !blackList.contains(property.getName())))
+        {
+          target[copyIndex] = property;
+          copyIndex++;
+        }
+      }
+      
+      // return the array, creating a new array if necessary
+      if (sourceSize != copyIndex)
+      {
+        if (copyIndex != 0)
+        {
+          return Arrays.copyOf(target, copyIndex);          
+        }
+        else
+        {
+          return _EMPTY_PROPERTY_NODE_ARRAY;
+        }
+      }
+      else
+      {
+        return target;
       }
     }
 
@@ -2086,6 +2084,8 @@ public class StyleSheetDocument
       return null;
     }
 
+    private static final PropertyNode[] _EMPTY_PROPERTY_NODE_ARRAY = new PropertyNode[0];
+
     // The set of properties (PropertyNodes) defined by this style
     private ArrayList<PropertyNode> _properties;
     
@@ -2102,110 +2102,55 @@ public class StyleSheetDocument
     private int _relativeFontSize;
   }
 
-  // Private Iterator implementation which strips null values
-  // from a wrapped Iterator.  StyleEntry uses this to avoid
-  // exposing null properties which result from removal of duplicate
-  // properties (really, nulling out of duplicate properties).
-  private static class NonNullIterator<T> implements Iterator<T>
-  {
-    public NonNullIterator(Iterator<T> wrappedIterator)
-    {
-      _wrappedIterator = wrappedIterator;
-
-      // Initialize the cache next element
-      _next = _getNonNullNext();
-    }
-
-    public boolean hasNext()
-    {
-      return (_next != null);
-    }
-
-    public T next()
-    {
-      T next = _next;
-      _next = _getNonNullNext();
-
-      return next;
-    }
-
-    public void remove()
-    {
-      _wrappedIterator.remove();
-    }
-
-    // Returns the next non null value in the wrapped enum
-    private T _getNonNullNext()
-    {
-      while (_wrappedIterator.hasNext())
-      {
-        T next = _wrappedIterator.next();
-
-        if (next != null)
-          return next;
-      }
-
-      return null;
-    }
-
-    // The wrapped enumeration
-    private Iterator<T> _wrappedIterator;
-
-    // The next non-null value in the wrapped enumeration
-    private T _next;
-  }
-
-  // A silly Iterator which converts "font-size" PropertyNodes to
-  // absolute values, using a specified relative font size
-  private static class FontSizeConverter implements Iterator<PropertyNode>
-  {
-    public FontSizeConverter(
-      Iterator<PropertyNode> wrappedIterator,
-      int relativeFontSize
-      )
-    {
-      _wrappedIterator = wrappedIterator;
-      _relativeFontSize = relativeFontSize;
-    }
-
-    public boolean hasNext()
-    {
-      return _wrappedIterator.hasNext();
-    }
-
-    public void remove()
-    {
-      _wrappedIterator.remove();
-    }
-
-    public PropertyNode next()
-    {
-      PropertyNode property = _wrappedIterator.next();
-
-      if ((_relativeFontSize == 0) ||
-           !_FONT_SIZE_NAME.equals(property.getName()))
-      {
-        return property;
-      }
-
-      return _getRealFontSize(property, _relativeFontSize);
-    }
-
-    // The wrapped enumeration
-    private Iterator<PropertyNode> _wrappedIterator;
-    private int         _relativeFontSize;
-  }
-
   private StyleSheetNode[] _styleSheets;
   private final String     _documentVersion;
   private final long       _documentTimestamp;
-
-  static final String _FONT_SIZE_NAME = "font-size";
+  
+  private static final Set<String> _IE_SUPPRESSED_PROPERTIES = new HashSet<String>(Arrays.asList(
+    "border-radius", "box-shadow"));
+  
+  
+  private static final Set<String> _DESTYLED_SUPPRESSED_PROPERTIES = new HashSet<String>(Arrays.asList(
+    "background-attachment", "background-color", "background-image", "background-position", "background-repeat", "background",
+    "border-collapse", "border-color", "border-spacing", "border-style",
+    "border-top", "border-right", "border-bottom", "border-left",
+    "border-top-color", "border-right-color", "border-bottom-color", "border-left-color",
+    "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", "border-width",
+    "border", "border-image", "border-radius",
+    "caption-side",
+    "color",
+    "content",
+    "cursor",
+    "font-family", "font-size", "font-style", "font-variant", "font-weight", "font",
+    "font-feature-settings", "font-kerning", "font-language-override", "font-size-adjust", "font-stretch", "font-variant-alternates",
+    "font-variant-caps", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
+    "hanging-punctuation",
+    "hyphens",
+    "icon",
+    "letter-spacing",
+    "line-break",
+    "list-style-image", "list-style-position", "list-style-type", "list-style",
+    "margin-right", "margin-left", "margin-top", "margin-bottom", "margin",
+    "marquee-direction", "marquee-loop", "marquee-speed", "marquee-style",
+    "opacity",
+    "outline-color", "outline-style", "outline-width", "outline", "outline-offset",
+    "overflow-style", "overflow-wrap",
+    "padding-top", "padding-right", "padding-bottom", "padding-left", "padding",
+    "resize",
+    "rotation", "rotation-point",
+    "target-new", "target-position",
+    "text-align", "text-align-last", "text-decoration", "text-indent", "text-justify", "text-transform", "text-overflow",
+    "transition", "transition-delay", "transition-duration", "transition-property", "transition-timing-function",
+    "vertical-align",
+    "white-space",
+    "word-break", "word-spacing", "word-wrap"));
+  
+  private static final String _FONT_SIZE_NAME = "font-size";
 
   // Unit strings.  For now, we only support points and pixels
   // for relative font sizes.
-  static final String _POINT_UNITS    = "pt";
-  static final String _PIXEL_UNITS    = "px";
+  private static final String _POINT_UNITS    = "pt";
+  private static final String _PIXEL_UNITS    = "px";
 
   // A StyleNode used as a placeholder for a style which couldn't be resolved
   private static final StyleNode _ERROR_STYLE_NODE = new StyleNode("error",