You are viewing a plain text version of this content. The canonical link for it is here.
Posted to adffaces-commits@incubator.apache.org by jw...@apache.org on 2007/03/27 00:02:14 UTC

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

Author: jwaldman
Date: Mon Mar 26 17:02:12 2007
New Revision: 522667

URL: http://svn.apache.org/viewvc?view=rev&rev=522667
Log:
move the url encoding into the code where we generate the css instead of earlier when we parse the css or xss files.
This way we can centralize the url encoding instead of having it in the xss & css code pathes.
Also doing so will allow us to better compare StyleSheetDocument's objects contents if we want to to do things like:
a. output the StyleSheetDocument's hashcode/id into the generated css's filename so that when the skin's css properties change, the generated
file name will change so the browser won't cache the file. The version alone is not enough since it might not get updated often enough.
b. allow portlet consumer's to tell us they want to suppress our stylesheet url in the page so they can use their stylesheet instead for
performance reasons, and we can refuse and fallback to the portal skin instead if the stylesheet documents' hashcodes don't match.

If we encoded the url too early, then the StyleSheetDocuments objects will have less of a chance to match due to the possibility of
different encodings.

Modified:
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetParserUtils.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSGenerationUtils.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSUtils.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/PropertyNodeParser.java

Modified: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetParserUtils.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetParserUtils.java?view=diff&rev=522667&r1=522666&r2=522667
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetParserUtils.java (original)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetParserUtils.java Mon Mar 26 17:02:12 2007
@@ -397,35 +397,6 @@
         }
         else
         {
-          if (_containsURL(propertyValue))
-          {
-            String resolvedUrl = _resolveURL(baseURI,
-                                            propertyValue,
-                                            sourceName,
-                                            selectorName,
-                                            propertyName);
-            
-            propertyNode = new PropertyNode(propertyName, resolvedUrl);
-          }
-          else if (_URI_PROPERTIES.contains(propertyName))
-          { 
-            // Make sure it's a legit value for an URL
-            if (!_SPECIAL_URI_VALUES.contains(propertyValue))
-            {
-              // TODO: Add a list of property names expecting an URL here, 
-              // "content" maybe?
-              _LOG.warning("An url value delimited by url() is expected for " +
-                           "the property '" +
-                           propertyName + 
-                           "' in selector '" +
-                           selectorName +
-                           "' in style sheet '" +
-                           sourceName + 
-                           "'. Found: '" + 
-                           propertyValue + "'.");
-            }
-          }
-
           noTrPropertyList.add(propertyNode);
         }
       }
@@ -885,138 +856,8 @@
     }
   }
   
-  private static String _resolveURL(
-      String baseURI,
-      String url,
-      String sourceName,
-      String selectorName,
-      String propertyName)
-  {
-    int endIndex = -1;
-    int index = url.indexOf("url(");
-    StringBuilder builder = new StringBuilder();
-    // this loop takes care of the usecase where there can be more than
-    // one url, like this: 
-    // background-image: url("/skins/purple/images/btns.gif"), 
-    // url("/skins/purple/images/checkdn.gif");
-    while(index >= 0)
-    {
-      // Appends values before url()
-      builder.append(url, endIndex + 1, index);
-      
-      endIndex = url.indexOf(')', index + 3);
-      String uri = url.substring(index + 4, endIndex);
-
-      // Trim off 
-      int uriLength = uri.length();
-      if (uriLength > 0)
-      {
-        if ((uri.charAt(0) == '\'' && uri.charAt(uriLength - 1) == '\'') ||
-            (uri.charAt(0) == '"' && uri.charAt(uriLength - 1) == '"'))
-        {
-          uri = uri.substring(1, uriLength - 1);
-          uriLength = uriLength - 2;
-        }
-      }
 
-      if(uriLength == 0)
-      {
-        // url() or url('') found, should not happen.
-        _LOG.warning("An empty URL was found in selector '" +
-                     selectorName +
-                     "' in style sheet '" +
-                     sourceName + "'.");
-      }
-      
-      builder.append("url(");
-      // At this point we have the uri -- the part within the url().
-      // resolve just that part, and put it back within the url()
-      // don't do this for icons; resolve their css properties, but
-      // not their content urls, since these are resolved in the ImageIcon classes.
-      if (!(_isIcon(selectorName) && "content".equals(propertyName)))
-      {
-        String resolvedURI = _resolveCSSURI(baseURI, uri, sourceName, selectorName);
-        builder.append(resolvedURI);
-      }
-      else
-        builder.append(uri);
-      builder.append(')');      
-
-      
-      index = url.indexOf("url(", endIndex);
-    }
-    
-    builder.append(url, endIndex + 1, url.length());
 
-    // Don't change anything
-    return builder.toString();
-  }
-  
-  // this is called to resolve the uri that is used in the generated CSS file
-  // do not call this method if the selector is an icon selector, since the icon url
-  // resolution happens in the ImageIcon classes.
-  private static String _resolveCSSURI (
-  String baseURI,
-  String uri,
-  String sourceName,
-  String selectorName)
-  {
-    // defaults to not converting the uri
-    // this handles the case where the uri starts with http:
-    String resolvedURI = uri;
-    FacesContext facesContext = FacesContext.getCurrentInstance();
-    assert(facesContext != null);
-    ExternalContext externalContext = facesContext.getExternalContext();
-    
-    if(uri.charAt(0) == '/')
-    {
-      int uriLength = uri.length();
-      // A transformation is required
-      if(uriLength > 1 && uri.charAt(1) == '/')
-      {
-        // Double slashes, trim one and do not add context root before
-        resolvedURI = uri.substring(1, uriLength);
-      }
-      else
-      {
-        // Single slash, add context path.
-        String contextPath = externalContext.getRequestContextPath();
-        
-        assert contextPath.charAt(0) == '/';
-        //if(contextPath.charAt(0) != '/')
-        //{
-        //  // Should not happen, but never too prudent
-        //  builder.append('/');
-        //}
-        
-        assert contextPath.charAt(contextPath.length() - 1) != '/';
-        //if(contextPath.charAt(contextPath.length() - 1) == '/')
-        //{
-        //  // Should not happen, but better safe than sorry.
-        //  builder.append(contextPath, 0, contextPath.length() - 1);
-        //}
-        //else
-        //{
-        StringBuilder builder = new StringBuilder(contextPath.length() + uri.length());
-        builder.append(contextPath);
-        //}
-        builder.append(uri);
-        resolvedURI = builder.toString();
-      }
-    }
-    else if(_isRelativeURI(uri))
-    {
-      // Convert relative URI values to absolute, since
-      // relative values will be resolved relative to the
-      // generated style sheet, not the source CSS file.
-      resolvedURI = _getAbsoluteURIValue(baseURI, uri, sourceName, selectorName);
-    }
-    return externalContext.encodeResourceURL(resolvedURI);
-
-  }
-
-
-  
 
   // Tests whether the specified property value is an "url" property.
   private static boolean _isURLValue(String propertyValue)
@@ -1036,72 +877,6 @@
     return trimQuotes(uri);
   }
 
-  // Tests whether the specified uri is relative
-  private static boolean _isRelativeURI(String uri)
-  {
-    return ((uri.charAt(0) != '/') && (uri.indexOf(':') < 0));
-  }
-
-
-  // Returns an absolute url value.
-  // this strips off any ../
-  private static String _getAbsoluteURIValue(
-    String baseURI,
-    String uri,
-    String sourceName,
-    String selectorName)
-  {
-    String strippedURI = uri;
-    String strippedBaseURI = baseURI;
-
-    // Strip off leading "../" segments from the uri
-    while (strippedURI.startsWith("../"))
-    {
-      int lastSepIndex = strippedBaseURI.lastIndexOf('/');
-      if (lastSepIndex < 0)
-      {
-        _LOG.warning("Invalid image uri '" +
-                     uri +
-                     "' in selector '" +
-                     selectorName +
-                     "' in style sheet '" +
-                     sourceName);
-
-        break;
-      }
-
-      strippedURI = strippedURI.substring(3);
-      strippedBaseURI = strippedBaseURI.substring(0, lastSepIndex);
-    }
-
-    StringBuilder builder = new StringBuilder(strippedBaseURI.length() +
-                                             strippedURI.length() +
-                                             2);
-    builder.append(strippedBaseURI);
-    builder.append("/");
-    builder.append(strippedURI);
-
-    return builder.toString();
-  }
-  
-  /**
-   * Determines if the specified value contains a CSS url. The URLs are
-   * detected but finding usage of url() function.
-   * 
-   * @param value
-   * 
-   * @return <code>true</code> if the specified value contains an URL, 
-   *         <code>false</code> otherwise.
-   */
-  private static boolean _containsURL(String value)
-  {
-    if(value == null)
-    {
-      return false;
-    }
-    
-    return value.indexOf("url(") >= 0;
-  }
   
   // returns true if the selectorName indicates that it is an icon.
   private static boolean _isIcon(String selectorName)
@@ -1197,23 +972,6 @@
   private static final String _PROPERTY_INHIBIT = "inhibit";
   private static final String _PROPERTY_TEXT_ANTIALIAS = "text-antialias";
 
-  // Set of values that are legal for url() values
-  private static final Set<String> _URI_PROPERTIES = new HashSet<String>();
-  static
-  {
-    _URI_PROPERTIES.add("background-image");
-    _URI_PROPERTIES.add("cue-after");
-    _URI_PROPERTIES.add("cue-before");
-    _URI_PROPERTIES.add("list-style-image");
-  }
-
-  // Set of values that are legal for url() values
-  private static final Set<String> _SPECIAL_URI_VALUES = new HashSet<String>();
-  static
-  {
-    _SPECIAL_URI_VALUES.add("none");
-    _SPECIAL_URI_VALUES.add("inherit");
-  }
 
   static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(
     SkinStyleSheetParserUtils.class);

Modified: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java?view=diff&rev=522667&r1=522666&r2=522667
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java (original)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java Mon Mar 26 17:02:12 2007
@@ -354,6 +354,7 @@
       entryCache = _entryCache;
 
       // Get the document up front too.
+      // Returns the StyleSheetDocument, parsing the source file if necessary
       // this sets up _shortStyleClassMap and _namespacePrefixes
       document = _getStyleSheetDocument(context);
       if (document == null)
@@ -364,6 +365,8 @@
     }
 
     // Look up the style sheet
+    // The Key class is a private static class that is used for hashing. It implements
+    // hashCode and equals which are based on locale, direction, browser, version, platform.
     Key key = new Key(context);
     Entry entry = _getEntry(cache, key, checkModified);
     if (entry != null)
@@ -422,6 +425,8 @@
   // 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.
+  // It also caches it in the "normal" cache (the one that is based on the StyleContext),
+  // and the entry cache (the one that is based on the StyleSheetNodes)
   private Entry _createEntry(
     StyleContext             context,
     StyleSheetDocument       document,
@@ -436,7 +441,7 @@
     // Next, get the fully resolved styles for this context. This will be
     // those StyleNodes that match the locale, direction, browser, etc -- the
     // info that is in the StyleContext.
-    StyleNode[] styles = _getStyles(context, document);
+    StyleNode[] styles = _getStyleContextResolvedStyles(context, document);
     if (styles == null)
       return null;
 
@@ -451,7 +456,9 @@
 
     _LOG.fine("Finished processing stylesheet {0}", uri);
 
-    // Create a new entry and cache it
+    // Create a new entry and cache it in the "normal" cache. The "normal" cache is one
+    // where the key is the Key object which is built based on information from the StyleContext,
+    // like browser, agent, locale, direction.
     Entry entry = new Entry(uri, new StyleMapImpl());
     cache.put(key, entry);
 
@@ -463,6 +470,8 @@
   }
 
   // Look in the entry cache for a compatible entry.
+  // A compatible entry is one with the same DerivationKey, which is essentially the 
+  // same StyleSheetNodes.
   private Entry _getCompatibleEntry(
     StyleContext             context,
     StyleSheetDocument       document,
@@ -556,8 +565,10 @@
   }
 
   // Returns an array of fully resolved StyleNodes for the
-  // specified context and document.
-  private StyleNode[] _getStyles(
+  // specified StyleContext  and StyleSheetDocument.
+  // This will be those StyleNodes that match the locale, direction, browser, etc -- the
+  // info that is in the StyleContext.
+  private StyleNode[] _getStyleContextResolvedStyles(
     StyleContext context,
     StyleSheetDocument document
     )
@@ -662,6 +673,7 @@
 
 
     CSSGenerationUtils.writeCSS(context,
+                                skin.getStyleSheetName(),
                                 styles,
                                 out,
                                 compressStyles,

Modified: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSGenerationUtils.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSGenerationUtils.java?view=diff&rev=522667&r1=522666&r2=522667
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSGenerationUtils.java (original)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSGenerationUtils.java Mon Mar 26 17:02:12 2007
@@ -31,6 +31,9 @@
 import java.util.Set;
 import java.util.Vector;
 
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent;
 import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.SkinSelectors;
@@ -54,6 +57,8 @@
     * 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 out The PrintWriter to write to
    * @param compressStyles This tells us whether or not we want to output the short names.
@@ -66,6 +71,7 @@
    */
   public static void writeCSS(
     StyleContext        context,
+    String              styleSheetName,
     StyleNode[]         styles,
     PrintWriter         out,
     boolean             compressStyles,
@@ -158,6 +164,9 @@
     out.println("/* CSS file generated on " + date + " */");
 
     // 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];
@@ -300,7 +309,10 @@
 
             out.print(propName);
             out.print(":");
-            out.print(propValue);
+            String resolvedPropValue = 
+              CSSUtils.resolvePropertyValue(styleSheetName, baseURI, propName, propValue);
+            out.print(resolvedPropValue);
+
           }
         }
 

Modified: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSUtils.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSUtils.java?view=diff&rev=522667&r1=522666&r2=522667
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSUtils.java (original)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSUtils.java Mon Mar 26 17:02:12 2007
@@ -20,11 +20,17 @@
 
 import java.awt.Color;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 import org.apache.myfaces.trinidad.util.ArrayMap;
 import org.apache.myfaces.trinidadinternal.style.CSSStyle;
 import org.apache.myfaces.trinidadinternal.style.PropertyParseException;
@@ -32,13 +38,137 @@
 import org.apache.myfaces.trinidadinternal.util.LRUCache;
 
 /**
- * CSS-related utilities. I think as we move away from xss, this code will
+ * CSS-related utilities. I think as we move away from xss, most of this code will
  * be removed.
  *
  * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/style/util/CSSUtils.java#0 $) $Date: 10-nov-2005.18:58:49 $
  */
 public class CSSUtils
 {
+
+  /**
+   * Resolve the propertyValue. For example, if the propertyValue is an url, then we run
+   * it through the encodeResourceURL method.
+   * @param styleSheetName - The Skin's stylesheet name.
+   * @param baseURI - An absolute base URI pointing to the directory which contains the 
+   * skin style sheet. You can compute this value by calling getBaseSkinStyleSheetURI and
+   * passing in the styleSheetName.
+   * @param propertyName - The css property name, like background-image
+   * @param propertyValue -The css property value, like url("skins/purple/error.gif");
+   * @return the propertyValue, possibly changed to a 'resolved' propertyValue. For instance,
+   * if it is an url, the return value will be the encoded url.
+   */
+  public static String resolvePropertyValue(
+    String styleSheetName,
+    String baseURI,
+    String propertyName,
+    String propertyValue)
+  {
+    // process propertyValue to encode if it is an url
+    if (_containsURL(propertyValue))
+    {
+      String resolvedUrl = _resolveURL(styleSheetName,
+                                      baseURI,
+                                      propertyValue);
+      return resolvedUrl;
+    }
+    else 
+    {
+      if (_URI_PROPERTIES.contains(propertyName))
+      { 
+        // Make sure it's a legit value for an URL
+        if (!_SPECIAL_URI_VALUES.contains(propertyValue))
+        {
+          // TODO: Add a list of property names expecting an URL here, 
+          // "content" maybe?
+          _LOG.warning("An url value delimited by url() is expected for " +
+                       "the property '" +
+                       propertyName + 
+                       "' in style sheet '" +
+                       styleSheetName + 
+                       "'. Found: '" + 
+                       propertyValue + "'.");
+        }
+      }
+      return propertyValue;
+    }
+  }
+
+
+  /**
+   * Given a Skin's stylesheet name that is being parsed,
+   * return an absolute base URI pointing to the
+   * directory which contains the skin style sheet.  We will use this
+   * base URI to ensure that URLs specified in the style sheet
+   * (eg. background-image URLs) are resolved appropriately
+   * (ie. not relative to the generated style sheet).
+   * 
+   * @param styleSheetName - The Skin's stylesheet name. E.g., "/skins/purple/purpleSkin.css"
+   * @return an absolute base URI pointing to the
+  // directory which contains the skin style sheet. e.g., "/trinidad-context/skins/purple"
+   */
+  public static String getBaseSkinStyleSheetURI(String styleSheetName)
+  {
+    // The styleSheetName is actually a context-relative URI.
+    // We need to strip off the file name and prepend the
+    // context path.
+    
+    // First, get the context path.
+    // Note that our options for obtaining the context path at
+    // this point are somewhat limited.  We could require that
+    // the caller pass this in, though this would require
+    // passing this information through many layers for non-obvious
+    // reasons.  Instead, we rely on the fact that we can obtain
+    // this information through the FacesContext (actually,
+    // through the ExternalContext), which we have access to here.
+    // This is slightly ugly, since this introduces a dependency
+    // from our CSS parsing code on JavaServer Faces, but until
+    // we find use cases where our skinning architecture will be
+    // applied in a non-Faces environment, this dependency seems
+    // accpetable.
+    FacesContext facesContext = FacesContext.getCurrentInstance();
+    assert(facesContext != null);
+
+    ExternalContext externalContext = facesContext.getExternalContext();
+    String contextPath = externalContext.getRequestContextPath();
+    assert(contextPath != null);
+
+    int contextPathLength = contextPath.length();
+
+    // Before we combine the context path and styleSheetName name to
+    // produce the base URI, make sure that these values are
+    // in the expected form
+    assert(contextPathLength > 0);
+    assert(contextPath.charAt(0) == '/');
+    assert(contextPath.charAt(contextPathLength - 1) != '/');
+    assert(styleSheetName.length() > 0);
+    assert(styleSheetName.charAt(0) != '/');
+
+    // Our internal css files are under /META-INF/adf/styles, though
+    // images are accessed via <contextPath>/adf/images.  As such,
+    // we also need to strip off the bonus "/META_INF" prefix from
+    // the source name.  Otherwise, image requests won't be
+    // resolved since /META-INF is not exposed via HTTP.
+    if (styleSheetName.startsWith("META-INF/"))
+      styleSheetName = styleSheetName.substring(9);
+
+    // Find the start of the file name part of the source name - we don't
+    // need this as part of the base URI
+    int lastSepIndex = styleSheetName.lastIndexOf('/');
+    
+    if (lastSepIndex == -1)
+      return contextPath;
+    else
+    {
+      StringBuilder buffer = new StringBuilder(
+                                    contextPathLength + lastSepIndex + 1);
+      buffer.append(contextPath);
+      buffer.append("/");
+      buffer.append(styleSheetName.substring(0, lastSepIndex));
+      return buffer.toString();
+    }
+  }
+
   /**
    * Parse an inline style into a CSSStyle object.
    */
@@ -598,6 +728,228 @@
 
     return str.substring(startIndex, endIndex);
   }
+  
+  /**
+   * Resolve the uri that will be output to the generated CSS file
+   * @param styleSheetName - the name of the Skin's stylesheet. We use this in any warning 
+   * messages.
+   * @param baseURI - absolute base URI pointing to the directory 
+   * which contains the skin style sheet.
+   * @param url - the url is the CSS property value that contains url(). For example, 
+   * "url('/skins/purple/abc.png')"
+   * @return The resolved uri. It will be run through the externalContext.encodeResourceURL
+   * method.
+   */  
+  private static String _resolveURL(
+    String styleSheetName,
+    String baseURI,
+    String url)
+  {
+    int endIndex = -1;
+    int index = url.indexOf("url(");
+    StringBuilder builder = new StringBuilder();
+    // this loop takes care of the usecase where there can be more than
+    // one url, like this: 
+    // background-image: url("/skins/purple/images/btns.gif"), 
+    // url("/skins/purple/images/checkdn.gif");
+    while(index >= 0)
+    {
+      // Appends values before url()
+      builder.append(url, endIndex + 1, index);
+      
+      endIndex = url.indexOf(')', index + 3);
+      String uri = url.substring(index + 4, endIndex);
+
+      // Trim off 
+      int uriLength = uri.length();
+      if (uriLength > 0)
+      {
+        if ((uri.charAt(0) == '\'' && uri.charAt(uriLength - 1) == '\'') ||
+            (uri.charAt(0) == '"' && uri.charAt(uriLength - 1) == '"'))
+        {
+          uri = uri.substring(1, uriLength - 1);
+          uriLength = uriLength - 2;
+        }
+      }
+
+      if(uriLength == 0)
+      {
+        // url() or url('') found, should not happen.
+        _LOG.warning("An empty URL was found in style sheet'" +
+                     styleSheetName + "'.");
+      }
+      
+      builder.append("url(");
+      // At this point we have the uri -- the part within the url().
+      // resolve just that part, and put it back within the url()
+      String resolvedURI = _resolveCSSURI(styleSheetName, baseURI, uri);
+      builder.append(resolvedURI);
+
+      builder.append(')');      
+
+      
+      index = url.indexOf("url(", endIndex);
+    }
+    
+    builder.append(url, endIndex + 1, url.length());
+
+    // Don't change anything
+    return builder.toString();
+  }
+  
+  /**
+   * Resolve the uri that will be output to the generated CSS file
+   * @param styleSheetName - the name of the Skin's stylesheet. We use this in any warning 
+   * messages.
+   * @param baseURI - absolute base URI pointing to the directory 
+   * which contains the skin style sheet.
+   * @param uri - a uri.
+   * @return The resolved uri. It will be run through the externalContext.encodeResourceURL
+   * method.
+   */
+  private static String _resolveCSSURI (
+  String styleSheetName,
+  String baseURI,
+  String uri)
+  {
+    // defaults to not converting the uri
+    // this handles the case where the uri starts with http:
+    String resolvedURI = uri;
+    FacesContext facesContext = FacesContext.getCurrentInstance();
+    assert(facesContext != null);
+    ExternalContext externalContext = facesContext.getExternalContext();
+    
+    if(uri.charAt(0) == '/')
+    {
+      int uriLength = uri.length();
+      // A transformation is required
+      if(uriLength > 1 && uri.charAt(1) == '/')
+      {
+        // Double slashes, trim one and do not add context root before
+        resolvedURI = uri.substring(1, uriLength);
+      }
+      else
+      {
+        // Single slash, add context path.
+        String contextPath = externalContext.getRequestContextPath();
+        
+        assert contextPath.charAt(0) == '/';
+        //if(contextPath.charAt(0) != '/')
+        //{
+        //  // Should not happen, but never too prudent
+        //  builder.append('/');
+        //}
+        
+        assert contextPath.charAt(contextPath.length() - 1) != '/';
+        //if(contextPath.charAt(contextPath.length() - 1) == '/')
+        //{
+        //  // Should not happen, but better safe than sorry.
+        //  builder.append(contextPath, 0, contextPath.length() - 1);
+        //}
+        //else
+        //{
+        StringBuilder builder = new StringBuilder(contextPath.length() + uri.length());
+        builder.append(contextPath);
+        //}
+        builder.append(uri);
+        resolvedURI = builder.toString();
+      }
+    }
+    else if(_isRelativeURI(uri))
+    {
+      // Convert relative URI values to absolute, since
+      // relative values will be resolved relative to the
+      // generated style sheet, not the source CSS file.
+      // e.g., if uri is "../../skins/purple/xyz.gif" and baseURI is /trinidad-context/skins/purple 
+      // we get "/trinidad-context/skins/purple/xyz.gif"
+      // 
+      resolvedURI = _getAbsoluteURIValue(styleSheetName, baseURI, uri);
+    }
+    return externalContext.encodeResourceURL(resolvedURI);
+
+  }
+
+  /**
+   * Tests whether the specified uri is relative
+   * @param uri - a uri
+   * @return true if the uri is a relative uri. That is, it doesn't start with '/' and
+   * it doesn't have a ':' in the string.
+   */
+  private static boolean _isRelativeURI(String uri)
+  {
+    return ((uri.charAt(0) != '/') && (uri.indexOf(':') < 0));
+  }
+
+  /**
+   * Convert a relative URI values to an absolute URI value.
+   * For example, if the baseURI is "/trinidad-context/skins/purple" and 
+   * the uri is "../../skins/purple/xyz.gif", we return 
+   * @param styleSheetName - the name of the Skin's stylesheet. We use this in any warning 
+   * messages.
+   * @param baseURI - absolute base URI pointing to the directory 
+   * which contains the skin style sheet. This is used to figure out the absolute uri of the uri
+   * parameter.
+   * @param uri - a uri. If this is an uri that begins with "../", then
+   * we convert it to be an absolute url that has no "../" at the start.
+
+   * @return An uri that does not begin with one or more "../" strings.
+   */
+  private static String _getAbsoluteURIValue(
+    String styleSheetName,
+    String baseURI,
+    String uri)
+  {
+    String strippedURI = uri;
+    String strippedBaseURI = baseURI;
+
+    // Strip off leading "../" segments from the uri
+    while (strippedURI.startsWith("../"))
+    {
+      int lastSepIndex = strippedBaseURI.lastIndexOf('/');
+      if (lastSepIndex < 0)
+      {
+        _LOG.warning("Invalid image uri '" +
+                     uri +
+                     "' in style sheet '" +
+                     styleSheetName);
+
+        break;
+      }
+
+      strippedURI = strippedURI.substring(3);
+      strippedBaseURI = strippedBaseURI.substring(0, lastSepIndex);
+    }
+
+    StringBuilder builder = new StringBuilder(strippedBaseURI.length() +
+                                             strippedURI.length() +
+                                             2);
+    builder.append(strippedBaseURI);
+    builder.append("/");
+    builder.append(strippedURI);
+
+    return builder.toString();
+  }
+  
+  /**
+   * Determines if the specified value contains a CSS url. The URLs are
+   * detected by finding usage of url() function.
+   * 
+   * @param value
+   * 
+   * @return <code>true</code> if the specified value contains an URL, 
+   *         <code>false</code> otherwise.
+   */
+  private static boolean _containsURL(String value)
+  {
+    if(value == null)
+    {
+      return false;
+    }
+    
+    return value.indexOf("url(") >= 0;
+  }  
+  
+  
 
   // CSS values
   private static final String _NORMAL_STYLE   = "normal";
@@ -701,5 +1053,26 @@
     "smaller",  10,
     "larger",   14
   };
+  
+
+  // Set of values that are legal for url() values
+  private static final Set<String> _URI_PROPERTIES = new HashSet<String>();
+  static
+  {
+    _URI_PROPERTIES.add("background-image");
+    _URI_PROPERTIES.add("cue-after");
+    _URI_PROPERTIES.add("cue-before");
+    _URI_PROPERTIES.add("list-style-image");
+  }
+
+  // Set of values that are legal for url() values
+  private static final Set<String> _SPECIAL_URI_VALUES = new HashSet<String>();
+  static
+  {
+    _SPECIAL_URI_VALUES.add("none");
+    _SPECIAL_URI_VALUES.add("inherit");
+  }  
+  
+  private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(CSSUtils.class);
 
 }

Modified: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/PropertyNodeParser.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/PropertyNodeParser.java?view=diff&rev=522667&r1=522666&r2=522667
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/PropertyNodeParser.java (original)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/PropertyNodeParser.java Mon Mar 26 17:02:12 2007
@@ -81,11 +81,6 @@
         _LOG.warning(error);
         return null;
       }
-      // resolve urls in the xss
-      if (_value!= null  && _isURLValue(_value))
-      {
-        _value = _resolveURL(_value);
-      }
       return new PropertyNode(_name, _value);
     }
     if (localName.equals(COLOR_PROPERTY_NAME))
@@ -131,110 +126,6 @@
     else
       _whitespace = _whitespace + whitespace;
   }
-
-  private static String _resolveURL(
-      String url)
-  {
-    int endIndex = -1;
-    int index = url.indexOf("url(");
-    StringBuilder builder = new StringBuilder();
-    // this loop takes care of the usecase where there can be more than
-    // one url, like this: 
-    // background-image: url("/skins/purple/images/btns.gif"), 
-    // url("/skins/purple/images/checkdn.gif");
-    while(index >= 0)
-    {
-      // Appends values before url()
-      builder.append(url, endIndex + 1, index);
-      
-      endIndex = url.indexOf(')', index + 3);
-      String uri = url.substring(index + 4, endIndex);
-
-      // Trim off 
-      int uriLength = uri.length();
-      if (uriLength > 0)
-      {
-        if ((uri.charAt(0) == '\'' && uri.charAt(uriLength - 1) == '\'') ||
-            (uri.charAt(0) == '"' && uri.charAt(uriLength - 1) == '"'))
-        {
-          uri = uri.substring(1, uriLength - 1);
-          uriLength = uriLength - 2;
-        }
-      }
-
-      if(uriLength == 0)
-      {
-        // url() or url('') found, should not happen.
-        _LOG.warning("An empty URL was found in selector in xss file");
-      }
-      
-      builder.append("url(");
-
-      String resolvedURI = _resolveCSSURI(uri);
-      builder.append(resolvedURI);
-      builder.append(')');      
-
-      
-      index = url.indexOf("url(", endIndex);
-    }
-    
-    builder.append(url, endIndex + 1, url.length());
-
-    // Don't change anything
-    return builder.toString();
-  }
-  // this is called to resolve the uri that is used in the generated CSS file
-  // do not call this method if the selector is an icon selector, since the icon url
-  // resolution happens in the ImageIcon classes.
-  private static String _resolveCSSURI (
-  String uri)
-  {
-    // defaults to not converting the uri
-    // this handles the case where the uri starts with http:
-    String resolvedURI = uri;
-    FacesContext facesContext = FacesContext.getCurrentInstance();
-    assert(facesContext != null);
-    ExternalContext externalContext = facesContext.getExternalContext();
-    
-    if(uri.charAt(0) == '/')
-    {
-      int uriLength = uri.length();
-      // A transformation is required
-      if(uriLength > 1 && uri.charAt(1) == '/')
-      {
-        // Double slashes, trim one and do not add context root before
-        resolvedURI = uri.substring(1, uriLength);
-      }
-      else
-      {
-        // Single slash, add context path.
-        String contextPath = externalContext.getRequestContextPath();
-        assert contextPath.charAt(0) == '/';       
-        assert contextPath.charAt(contextPath.length() - 1) != '/';
-
-        StringBuilder builder = new StringBuilder(contextPath.length() + uri.length());
-        builder.append(contextPath);
-
-        builder.append(uri);
-        resolvedURI = builder.toString();
-      }
-    }
-    // now encode the resolved url and return that
-    return externalContext.encodeResourceURL(resolvedURI);
-  }
-
-
-  
-
-  // Tests whether the specified property value is an "url" property.
-  private static boolean _isURLValue(String propertyValue)
-  {
-    // URL property values start with "url("
-    return propertyValue.startsWith("url(");
-  }
-
-
-
 
   // Validates the value using a PropertyValidater.  Returns an error
   // message if there are validation errors.  Otherwise, returns null