myfaces-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sobr...@apache.org
Subject svn commit: r1523719 [1/2] - in /myfaces/trinidad/trunk: src/site/xdoc/devguide/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/ trinidad-impl/src/main/ja...
Date Mon, 16 Sep 2013 16:08:41 GMT
Author: sobryan
Date: Mon Sep 16 16:08:41 2013
New Revision: 1523719

URL: http://svn.apache.org/r1523719
Log:
NIDAD-2399: support css client side rules in skinning framework
 * Thanks for the patch Anand

Modified:
    myfaces/trinidad/trunk/src/site/xdoc/devguide/skinning.xml
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinCSSDocumentHandler.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetNode.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetParserUtils.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/xml/parse/StyleNode.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java
    myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetNodeEqualsTest.java

Modified: myfaces/trinidad/trunk/src/site/xdoc/devguide/skinning.xml
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/src/site/xdoc/devguide/skinning.xml?rev=1523719&r1=1523718&r2=1523719&view=diff
==============================================================================
--- myfaces/trinidad/trunk/src/site/xdoc/devguide/skinning.xml (original)
+++ myfaces/trinidad/trunk/src/site/xdoc/devguide/skinning.xml Mon Sep 16 16:08:41 2013
@@ -61,6 +61,14 @@
           </li>
           <li>
             <a href="#Skinning_CSS_features">Skinning CSS features</a>
+            <ul>
+              <li><a href="#tr-rules">Re-using selectors and properties via -tr-rules</a>
+              </li>
+              <li><a href="#server-side-at-rules">Server side at-rules</a>
+              </li>
+              <li><a href="#client-side-at-rules">Client side at-rules</a>
+              </li>
+            </ul>
           </li>
           <li>
             <a href="#Package_Skin_In_JAR">Package your Skin in a JAR file</a>
@@ -824,157 +832,159 @@ public class SkinTranslationMapDemo
       <a name="Skinning_CSS_features"></a>
       <section name="Skinning CSS features">
         
-        <P>
-        You can do things in a skin css file beyond what you can do in a regular css file. 
-        At runtime the skin framework processes the skin's css file and the
-        skin framework pulls out skinning properties and icons and registers them with the Skin object. 
-        The skinning framework merges styles together that use the -tr-rule-ref property. 
-        The skinning framework picks the styles based on the HTTP request information, like agent
-        and platform and merges them with the non-specific styles.
-        This section will go into more detail on these features and how you can use them in your 
-        skinning css file.
-        </P>
-        <P>
-        <ul>
-        <li>
-        Skin properties e.g., af|breadcrumbs{<strong>-tr-show-last-item:false</strong>}.
-        Skin properties for a component are documented in the skin-selectors documentation. Skin
-        properties are useful if you want to control the rendering of a component that css alone
-        cannot do, like displaying the last item or not (well, css might be able to do that). 
-        The writer of the renderer may choose to expose skin properties to enable the application
-        developer to have more control over the way the component renders without having to 
-        write their own renderer. Another example is af|train {-tr-visible-stop-count: 10}. This 
-        allows you to change the number of visible train stops.
-        </li> 
-        <li>
-        <strong>-tr-inhibit</strong> - > e.g., af|foo {-tr-inhibit: padding; color: red}
-        This css property is used to inhibit/reset css properties that you are inheriting from a base skin.
-        "-tr-inhibit: all" will clear out all the inherited properties. "-tr-inhibit: <i>property-name</i>" will inhibit the 
-        property name; "-tr-inhibit: padding", for example, will clear out any padding
-        that you have inherited. You need to make sure your <i>property-name</i> matches that in the base skin. If the
-        base skin has padding-right: 6px and you inhibit padding, you will still get the padding-right. You need
-        to inhibit padding-right. Also note that inhibitions happen when the skinning framework processes the skin's css file,
-        and not based on cascading rules in the browser. Therefore the skin selector has to match and the property name has
-        to match exactly to what you are inhibiting. -tr-inhibit works for style selectors, icons 
-        (selectors that end with -icon) and skinning properties.
-        <source>
-          <![CDATA[
-          
-          Skin A:
-            af|breadCrumbs { -tr-show-last-item: false; }
+        <a name="tr-rules"></a>
+        <subsection name="Re-using selectors and properties via -tr-rules">
+          <P>
+          You can do things in a skin css file beyond what you can do in a regular css file.
+          At runtime the skin framework processes the skin's css file and the
+          skin framework pulls out skinning properties and icons and registers them with the Skin object.
+          The skinning framework merges styles together that use the -tr-rule-ref property.
+          The skinning framework picks the styles based on the HTTP request information, like agent
+          and platform and merges them with the non-specific styles.
+          This section will go into more detail on these features and how you can use them in your
+          skinning css file.
+          </P>
+          <P>
+          <ul>
+          <li>
+          Skin properties e.g., af|breadcrumbs{<strong>-tr-show-last-item:false</strong>}.
+          Skin properties for a component are documented in the skin-selectors documentation. Skin
+          properties are useful if you want to control the rendering of a component that css alone
+          cannot do, like displaying the last item or not (well, css might be able to do that).
+          The writer of the renderer may choose to expose skin properties to enable the application
+          developer to have more control over the way the component renders without having to
+          write their own renderer. Another example is af|train {-tr-visible-stop-count: 10}. This
+          allows you to change the number of visible train stops.
+          </li>
+          <li>
+          <strong>-tr-inhibit</strong> - > e.g., af|foo {-tr-inhibit: padding; color: red}
+          This css property is used to inhibit/reset css properties that you are inheriting from a base skin.
+          "-tr-inhibit: all" will clear out all the inherited properties. "-tr-inhibit: <i>property-name</i>" will inhibit the
+          property name; "-tr-inhibit: padding", for example, will clear out any padding
+          that you have inherited. You need to make sure your <i>property-name</i> matches that in the base skin. If the
+          base skin has padding-right: 6px and you inhibit padding, you will still get the padding-right. You need
+          to inhibit padding-right. Also note that inhibitions happen when the skinning framework processes the skin's css file,
+          and not based on cascading rules in the browser. Therefore the skin selector has to match and the property name has
+          to match exactly to what you are inhibiting. -tr-inhibit works for style selectors, icons
+          (selectors that end with -icon) and skinning properties.
+          <source>
+            <![CDATA[
 
-          Skin B extends Skin A:
-            af|breadCrumbs { -tr-inhibit: -tr-show-last-item;}
+            Skin A:
+              af|breadCrumbs { -tr-show-last-item: false; }
 
-          Skin A does not show the last breadcrumb. Skin B does show the last breadcrumb.
-          
-          --
-          Skin A
-            .SomeStyleClass { 
-                color: red;
-                font-size: 11px;
-            }
-          Skin B
-            .SomeStyleClass { 
-                -tr-inhibit: color;
+            Skin B extends Skin A:
+              af|breadCrumbs { -tr-inhibit: -tr-show-last-item;}
+
+            Skin A does not show the last breadcrumb. Skin B does show the last breadcrumb.
+
+            --
+            Skin A
+              .SomeStyleClass {
+                  color: red;
+                  font-size: 11px;
+              }
+            Skin B
+              .SomeStyleClass {
+                  -tr-inhibit: color;
+              }
+
+            Skin A has color: red, but Skin B has no color, since it has been inhibited.
+
+            --
+            Skin A
+            .SomeStyleClass {
+              color: red;
+              font-size: 11px;
+              -tr-inhibit: color;
             }
-            
-          Skin A has color: red, but Skin B has no color, since it has been inhibited.
-          
-          --
-          Skin A
-          .SomeStyleClass { 
-            color: red;
-            font-size: 11px;
-            -tr-inhibit: color;
-          }
-          
-          This definition makes no sense. If your intent is to remove the color attribute, 
-          then you should simple say:
-          .SomeStyleClass {font-size: 11px}
-          You shouldn't be setting a property, then inhibiting it in the same definition, or even the same skin.
-          Inhibits happen first in the same rule, then the specific styles, so if you inhibit a property, and also
-          define a property in the same rule, it won't be inhibited.
-          However, if you define .SomeStyleClass {-tr-inhibit: color;} later in Skin A, it will be inhibited.
-          Again, do not inhibit within the same skin. It takes more programming cycles to do the merge, and your css
-          isn't as clean.
-          ]]>
-        </source>
-        </li>        
-        <li>
-        <strong>-tr-rule-ref</strong> - > This is used to include other styles in your style see :alias
-        section. Application developers probably won't use this. This is meant for component developers to use when
-        they are setting up the base skin for their components.
-        Here are some examples of how you can use -tr-rule-ref and what the generated css would be.
-        <source>
-          <![CDATA[
-          Example 1:
-          
-          Skin A
-          
-              .AFSomeAlias:alias {color: red}
-              .foo {-tr-rule-ref: selector(".AFSomeAlias:alias")}
-          
-          Skin B extends Skin A
-          
-              .AFSomeAlias:alias {color: blue}
-          
-          Result
-          
-          If you run Skin A, you will see in your CSS
-              .foo {color: red}
-          
-          In you run Skin B, you will see in your CSS
-              .foo {color: blue}
-          because the alias that .foo includes (.AFSomeAlias:alias) in Skin A has 
-          overwritten its color property in Skin B to blue.
-                  
-          ---
-          Example 2:
-          
-          Skin A
-          
-              .myClass {color: orange}
-          
-          Skin B extends Skin A
-          
-              .MyBlueColor:alias {color: blue}
-              .myClass {color: pink; -tr-rule-ref: selector(".MyBlueColor:alias")}   
-          
-          If you run Skin A, you will see in your CSS
-              .myClass {color: orange}
-          
-          In you run Skin B, you will see in your CSS
-              .myClass {color: pink}
-          
-          In Skin B's definition for .myClass, all 'includes' (like -tr-rule-ref) get merged in first, 
-          then specific property definitions (like color in this case) get merged in last for the rule.
-          Therefore, color: pink overwrites -tr-rule-ref: selector(".MyBlueColor:alias");
-          However, if you define .myClass {-tr-rule-ref: selector(".MyBlueColor:alias")} later in Skin B, it will be blue.
-          
-          If you are unsure of what includes or inhibits resolve to, you can run your own skinning test like 
-          above, and look at the generated css file.
-          ]]>
-        </source>
-        </li>
-        <li>
-        <strong>-tr-property-ref</strong> - > This is used to include properties from one style into another.
-        The syntax of -tr-property-ref is: background-color: -tr-property-ref("af|foo","color"), where the first
-        parameter is the style from which the property is included and the second one is the name of the included property
-        in that style. 
-        There can be more than one occurrence of -tr-property-ref in a property value, for example:
-        background: -webkit-linear-gradient(top, -tr-property-ref("af|foo","color") 0%, -tr-property-ref("af|bar","color") 100%);
-        When the property included has the same name as in the including selector, the following syntax
-        can also be used: background-color: -tr-property-ref("af|foo") instead of: background-color: -tr-property-ref("af|foo",
-        "background-color"). This is useful when only some properties from another style are needed to be included.
-        In this case, we won't need to include the entire style via a -tr-rule-ref rule, but only the needed property.
-        </li>
-        <li>
-        <strong>+/-</strong> - > This feature of the skinning framework allows you to set
-        a selector's color or font that is relative to another selector's color or font. 
-        This is useful if you have color ramps. You can change the color in one place. An example
-        for the color is:
-        <source>
+
+            This definition makes no sense. If your intent is to remove the color attribute,
+            then you should simple say:
+            .SomeStyleClass {font-size: 11px}
+            You shouldn't be setting a property, then inhibiting it in the same definition, or even the same skin.
+            Inhibits happen first in the same rule, then the specific styles, so if you inhibit a property, and also
+            define a property in the same rule, it won't be inhibited.
+            However, if you define .SomeStyleClass {-tr-inhibit: color;} later in Skin A, it will be inhibited.
+            Again, do not inhibit within the same skin. It takes more programming cycles to do the merge, and your css
+            isn't as clean.
+            ]]>
+          </source>
+          </li>
+          <li>
+          <strong>-tr-rule-ref</strong> - > This is used to include other styles in your style see :alias
+          section. Application developers probably won't use this. This is meant for component developers to use when
+          they are setting up the base skin for their components.
+          Here are some examples of how you can use -tr-rule-ref and what the generated css would be.
+          <source>
+            <![CDATA[
+            Example 1:
+
+            Skin A
+
+                .AFSomeAlias:alias {color: red}
+                .foo {-tr-rule-ref: selector(".AFSomeAlias:alias")}
+
+            Skin B extends Skin A
+
+                .AFSomeAlias:alias {color: blue}
+
+            Result
+
+            If you run Skin A, you will see in your CSS
+                .foo {color: red}
+
+            In you run Skin B, you will see in your CSS
+                .foo {color: blue}
+            because the alias that .foo includes (.AFSomeAlias:alias) in Skin A has
+            overwritten its color property in Skin B to blue.
+
+            ---
+            Example 2:
+
+            Skin A
+
+                .myClass {color: orange}
+
+            Skin B extends Skin A
+
+                .MyBlueColor:alias {color: blue}
+                .myClass {color: pink; -tr-rule-ref: selector(".MyBlueColor:alias")}
+
+            If you run Skin A, you will see in your CSS
+                .myClass {color: orange}
+
+            In you run Skin B, you will see in your CSS
+                .myClass {color: pink}
+
+            In Skin B's definition for .myClass, all 'includes' (like -tr-rule-ref) get merged in first,
+            then specific property definitions (like color in this case) get merged in last for the rule.
+            Therefore, color: pink overwrites -tr-rule-ref: selector(".MyBlueColor:alias");
+            However, if you define .myClass {-tr-rule-ref: selector(".MyBlueColor:alias")} later in Skin B, it will be blue.
+
+            If you are unsure of what includes or inhibits resolve to, you can run your own skinning test like
+            above, and look at the generated css file.
+            ]]>
+          </source>
+          </li>
+          <li>
+          <strong>-tr-property-ref</strong> - > This is used to include properties from one style into another.
+          The syntax of -tr-property-ref is: background-color: -tr-property-ref("af|foo","color"), where the first
+          parameter is the style from which the property is included and the second one is the name of the included property
+          in that style.
+          There can be more than one occurrence of -tr-property-ref in a property value, for example:
+          background: -webkit-linear-gradient(top, -tr-property-ref("af|foo","color") 0%, -tr-property-ref("af|bar","color") 100%);
+          When the property included has the same name as in the including selector, the following syntax
+          can also be used: background-color: -tr-property-ref("af|foo") instead of: background-color: -tr-property-ref("af|foo",
+          "background-color"). This is useful when only some properties from another style are needed to be included.
+          In this case, we won't need to include the entire style via a -tr-rule-ref rule, but only the needed property.
+          </li>
+          <li>
+          <strong>+/-</strong> - > This feature of the skinning framework allows you to set
+          a selector's color or font that is relative to another selector's color or font.
+          This is useful if you have color ramps. You can change the color in one place. An example
+          for the color is:
+          <source>
           <![CDATA[
           .BaseBG:alias { background-color: #0099ff; }
           .fooColorTest {
@@ -985,25 +995,30 @@ public class SkinTranslationMapDemo
             background-color: -#333333;
           }
 
-this resolves to
+    this resolves to
 
           .fooColorTest {background-color:#33ccff}
           .fooColorTestMinus {background-color:#0066cc}
           ]]>
-        </source>
-        The same thing works for fonts.
-        <source>
-          <![CDATA[    
-            .FontSizeTest:alias {font-size: 12pt;}
+          </source>
+          The same thing works for fonts.
+          <source>
+            <![CDATA[
+              .FontSizeTest:alias {font-size: 12pt;}
 
-            .fooFontTest {
-              -tr-rule-ref: selector(".FontSizeTest:alias");
-              font-size:+1pt;
-            }
-          ]]>
-        </source>        
-        </li>
-        </ul>
+              .fooFontTest {
+                -tr-rule-ref: selector(".FontSizeTest:alias");
+                font-size:+1pt;
+              }
+            ]]>
+          </source>
+          </li>
+          </ul>
+          </P>
+        </subsection>
+
+        <a name="server-side-at-rules"></a>
+        <subsection name="Server side at-rules">
         <P>
         You might not want your selector's css properties to be applied to all browsers, all platforms, 
         all locales, and both reading-directions. For example, you
@@ -1015,10 +1030,12 @@ this resolves to
         The skinning framework picks the styles based on the HTTP request information, like agent
         and platform and merges them with the styles without rules.       
         These css properties that match the 'rules' get merged with those outside of any 'rules'; 
-        the most specific rules that match a user's environment take precedence. 
+        the most specific rules that match a user's environment take precedence.
+        These rules are resolved at server side and matching selectors are rendered for the
+        user agent.
         (See an example in the css code below.)
 
-        The skinning framework currently supports these 'rules':
+        The skinning framework currently supports these server side 'rules':
         </P>
         <ul>
         <li>
@@ -1243,7 +1260,106 @@ this resolves to
         }        
         
         </source>
-        </P>
+       </subsection>
+
+        <a name="client-side-at-rules"></a>
+        <subsection name="Client side at-rules">
+          <P>
+          If you want to use rules that are supported out of the box by CSS like @document, @media etc, you can
+          specify those rules in the skin file and expect them to be rendered directly into the browser. These rules
+          will be evaluated by the browser and styles will get applied if the browser passes the conditions
+          mentioned in the rules.
+
+          The skinning framework currently supports these client side 'rules':
+          </P>
+
+          <ul>
+            <li>@document</li>
+            <li>@font-face</li>
+            <li>@keyframes</li>
+            <li>@media</li>
+            <li>@page</li>
+            <li>@supports</li>
+          </ul>
+
+          <P>
+          If you use any rule which is not listed in either server side at-rules or client side at-rules, it will be
+          rendered as client side rule similar to @media or @document.
+          <b>Note:</b>
+          Styles inside client side at-rules cannot be used with tr-rules. This is because, in server side, we do not
+          know if the referred style will be rendered.
+          On the contrary, tr-rules can be used inside client side at-rules to refer styles or properties define outside
+          client rules.
+
+          Some example usages:
+          </P>
+          <source>
+
+af|document {
+  background-color: white;
+  background-image: url('/skins/custom-base/top-band.png'), url('/skins/custom-base/texture.png');
+  background-repeat: repeat-x, repeat;
+  background-position: top left, top left;
+  background-size: 10px 61px, 6px 6px;
+}
+
+/* specify different background-image for min-device-pixel-ratio = 2 */
+@media only screen and (min-device-pixel-ratio: 2) {
+  af|document {
+    background-image: url('/skins/custom-base/top-band@2x.png'), url('/skins/custom-base/texture@2x.png');
+  }
+}
+
+/* specify different background-image for min-resolution: 2dppx*/
+@media only screen and (min-resolution: 2dppx) {
+  af|document {
+    background-image: url('/skins/custom-base/top-band@2x.png'), url('/skins/custom-base/texture@2x.png');
+  }
+}
+
+.Some:alias {
+  color: red;
+  background-color: green;
+}
+
+@media only screen {
+  af|mystyle {
+    -tr-rule-ref: selector(".Some:alias");
+  }
+  af|mystyle2 {
+    background-color: -tr-property-ref(".Some:alias","color");
+  }
+}
+          </source>
+          <p>
+          Client side at-rules can be nested in server side at-rules and vice versa. One slight deviation is for @page
+          and @font-face rules. These can be wrapped in a server side at-rule, but cannot contain a inner server side
+          at-rule because these rules directly contain CSS properties unlike other client side at-rules which contains
+          complete styles.
+          </p>
+          <source>
+@agent gecko {
+  @page:first {
+    margin:2in;
+  }
+}
+
+@keyframes mymove {
+  @agent gecko {
+    0% { top: 0; left: 0; }
+    30% { top: 50px; }
+    68%, 72% { left: 50px; }
+    100% { top: 100px; left: 100%; }
+  }
+
+  @agent ie {
+    0% { top: 1; left: 1; }
+    30% { top: 100px; }
+    100% { top: 200px; left: 100%; }
+  }
+}
+          </source>
+        </subsection>
       </section>
       <a name="Package_Skin_In_JAR"></a>
       <section name="Package your Skin in a JAR file">

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinCSSDocumentHandler.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinCSSDocumentHandler.java?rev=1523719&r1=1523718&r2=1523719&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinCSSDocumentHandler.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinCSSDocumentHandler.java Mon Sep 16 16:08:41 2013
@@ -26,15 +26,15 @@ import java.io.StringReader;
 
 import java.text.ParseException;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
-import java.util.ArrayList;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Set;
-import java.util.HashSet;
-import java.util.HashMap;
-import java.util.LinkedList;
 
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 import org.apache.myfaces.trinidad.share.io.InputStreamProvider;
@@ -42,7 +42,6 @@ import org.apache.myfaces.trinidad.share
 import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent;
 import org.apache.myfaces.trinidadinternal.share.io.CachingInputStreamProvider;
 import org.apache.myfaces.trinidadinternal.share.xml.ParseContext;
-import org.apache.myfaces.trinidadinternal.share.xml.ParseErrorUtils;
 import org.apache.myfaces.trinidadinternal.share.xml.XMLUtils;
 import org.apache.myfaces.trinidadinternal.style.util.ModeUtils;
 import org.apache.myfaces.trinidadinternal.style.util.NameUtils;
@@ -153,16 +152,21 @@ public class SkinCSSDocumentHandler
     
     for (int i = 0; i < selectorNum; i++)
     {
-       String selector = selectors.get(i);
-       CompleteSelectorNode node =
-         _createCompleteSelectorNode(selector,
+      String selector = selectors.get(i);
+
+      if (selector.startsWith(_AT_TOKEN))
+        selector = selector.replace(_AT_TOKEN, _AT);
+
+      CompleteSelectorNode node =
+        _createCompleteSelectorNode(selector,
                                      _propertyNodeList,
                                      _locales,
                                      _agentAtRuleMatcher,
                                      _selectorPlatforms,
                                      _getSelectorAccProperties(),
-                                     _mode);
-       _completeSelectorNodeList.add(node);
+                                     _mode, 
+                                     _clientRule);
+      _completeSelectorNodeList.add(node);
     }
     // reset flags
     _inStyleRule = false;
@@ -221,6 +225,12 @@ public class SkinCSSDocumentHandler
         else
           _parseImport(atRule);
       }
+      else if (atRule.startsWith(_AT_PAGE) || atRule.startsWith(_AT_FONT_FACE))
+      {
+        // @page and @font-face are client side selectors
+        // these should be rendered as @page { property: value }
+        _parseClientSideSelector(atRule);
+      }
       else if (atRule.startsWith(_AT_NAMESPACE))
       {
         _parseNamespace(_namespaceMap, atRule);
@@ -245,7 +255,12 @@ public class SkinCSSDocumentHandler
       {
         _parseCustomAtRule(_AT_MODE, atRule);
       }
-      // for now, ignore other atRules in a skinning css file
+      else if (atRule.startsWith(_AT))
+      {
+        // for all other rules that does not belong to server side
+        // assume that they are client rules
+        _parseCustomAtRule(null, atRule);
+      }
       
       // CSS spec says you ignore all @import rules after any other rules are processed
       // (except for @charset).
@@ -419,7 +434,23 @@ public class SkinCSSDocumentHandler
       stream.close();
     }
   }
-  
+
+  /**
+   * parses the special client side rules which are to be rendered as selectors
+   * and not rules
+   * @param atRule - client side rule content
+   * e.g. - @page:first { margin: 1in; }
+   * e.g. - @font-face { font-family: MyHelvetica; font-weight: bold; }
+   */
+  private void _parseClientSideSelector(String atRule)
+  {
+    // fool the parser by replacing '@' character so that it is parsed as a normal selector
+    // at the time we create CompleteSelectorNodes in endSelector we do the opposite
+    atRule = atRule.replace(_AT, _AT_TOKEN);
+    SkinCSSParser parser = new SkinCSSParser();
+    parser.parseCSSDocument(new StringReader(atRule), this);
+  }
+
   /** Get the atRule, and send its contents through the SkinCSSParser
    * again, using the current DocumentHandler object. The start/end
    * callbacks will be called again, but in the context of the atRule.
@@ -442,11 +473,13 @@ public class SkinCSSDocumentHandler
     _resetAtRuleTargetTypes(type);
 
   }
-  
+
   private void _resetAtRuleTargetTypes(
     String type)
   {
-    if (_AT_AGENT.equals(type))
+    if (type == null)
+      _clientRule = null;
+    else if (_AT_AGENT.equals(type))
       _agentAtRuleMatcher = null;
     else if (_AT_PLATFORM.equals(type))
       _selectorPlatforms = null;
@@ -474,7 +507,8 @@ public class SkinCSSDocumentHandler
     AgentAtRuleMatcher         agentMatcher,
     int[]                      selectorPlatforms,
     Set<String>                selectorAccProperties,
-    int                        mode)
+    int                        mode,
+    String                     clientRule)
   {
     // parse the selector to see if there is a :rtl or :ltr ending.
     // if so, then set the reading direction.
@@ -503,7 +537,8 @@ public class SkinCSSDocumentHandler
         agentMatcher,
         selectorPlatforms,
         selectorAccProperties,
-        mode);
+        mode,
+        clientRule);
   }
 
   /**
@@ -530,6 +565,7 @@ public class SkinCSSDocumentHandler
       Set<Locale> locales = completeSelectorNode.getLocales();
       Set<String> accProperties = completeSelectorNode.getAccessibilityProperties();
       int mode = completeSelectorNode.getMode();
+      String clientRule = completeSelectorNode.getClientRule();
 
       // loop through the skinStyleSheetNodeList to find a match
       // of direction, agents, platforms, etc.
@@ -540,7 +576,7 @@ public class SkinCSSDocumentHandler
       for (int i = skinStyleSheetNodes.size() - 1; i >= 0 && !match; --i)
       {
         SkinStyleSheetNode ssNode = skinStyleSheetNodes.get(i);
-        match = ssNode.matches(direction, agentMatcher, platforms, locales, accProperties, mode);
+        match = ssNode.matches(direction, agentMatcher, platforms, locales, accProperties, mode, clientRule);
 
         if (match)
           ssNode.add(completeSelectorNode.getSkinSelectorPropertiesNode());
@@ -549,8 +585,15 @@ public class SkinCSSDocumentHandler
       if (!match)
       {
         // no matching stylesheet node found, so create a new one
-        SkinStyleSheetNode ssNode =
-         new SkinStyleSheetNode(namespaceMap, direction, locales, agentMatcher, platforms, accProperties, mode);
+        SkinStyleSheetNode ssNode = new SkinStyleSheetNode(namespaceMap,
+                                                           direction,
+                                                           locales,
+                                                           agentMatcher,
+                                                           platforms,
+                                                           accProperties,
+                                                           mode,
+                                                           clientRule);
+
         ssNode.add(completeSelectorNode.getSkinSelectorPropertiesNode());
         skinStyleSheetNodes.add(ssNode);
       }
@@ -577,8 +620,12 @@ public class SkinCSSDocumentHandler
     {
       String types = atRule.substring(firstSpace, openBrace);
       String[] typeArray = types.split(",");
-      
-      if (_AT_AGENT.equals(type))
+
+      if (type == null)
+      {
+        _clientRule = atRule.substring(0, openBrace);
+      }
+      else if (_AT_AGENT.equals(type))
       {
         _agentAtRuleMatcher = new AgentAtRuleMatcher(typeArray);
       }
@@ -657,7 +704,7 @@ public class SkinCSSDocumentHandler
   
     return array;
   }
-   
+
    /**
     * 
     * @param atRule - the entire @rule's definition, including content.
@@ -674,9 +721,9 @@ public class SkinCSSDocumentHandler
       
     if (openBraceIndex == -1)
       return null;
-    else
-     return atRule.substring(openBraceIndex+1, endBraceIndex);
-   
+    else 
+      return atRule.substring(openBraceIndex+1, endBraceIndex);
+  
   }
 
   // Returns the accessibility properties in effect for the current selector
@@ -756,7 +803,8 @@ public class SkinCSSDocumentHandler
       AgentAtRuleMatcher         agentMatcher,
       int[]                      platforms,
       Set<String>                accProperties,
-      int                        mode
+      int                        mode,
+      String                     clientRule
       )
     {
       _node = new SkinSelectorPropertiesNode(selectorName, propertyNodes);
@@ -781,6 +829,7 @@ public class SkinCSSDocumentHandler
         _accProperties = null;
       }
       _mode = mode;
+      _clientRule = clientRule;
     }
     
     public SkinSelectorPropertiesNode getSkinSelectorPropertiesNode()
@@ -821,6 +870,11 @@ public class SkinCSSDocumentHandler
       return _mode;
     }
 
+    public String getClientRule()
+    {
+      return _clientRule;
+    }
+
     // Returns a copy of the int array
     private static int[] _copyIntArray(int[] array)
     {
@@ -840,6 +894,7 @@ public class SkinCSSDocumentHandler
     private final Set<Locale> _locales; 
     private final Set<String> _accProperties;
     private int _mode;
+    private final String _clientRule;
   }
 
   private static final String _AT_AGENT = "@agent";
@@ -851,6 +906,12 @@ public class SkinCSSDocumentHandler
   private static final String _AT_NAMESPACE = "@namespace";
   private static final String _AT_CHARSET = "@charset";
 
+  // special client side rules which need to be rendered as selectors
+  private static final String _AT_FONT_FACE = "@font-face";
+  private static final String _AT_PAGE = "@page";
+  private static final String _AT_TOKEN = "_AT_TOKEN_";
+  private static final String _AT = "@";
+
 
   // below are properties that we set and reset
   // as the methods of this documentHandler get called.
@@ -873,6 +934,9 @@ public class SkinCSSDocumentHandler
   //the mode for which the parsed selectors are valid.
   private int _mode = ModeUtils.MODE_DEFAULT;
 
+  // client side rule to be rendered as is to the css, eg: @media, @document, @keyframes
+  private String _clientRule;
+
   // Stack of accessibility property sets.  While java.util.Stack has the
   // push/pop API that we want, we don't need the synchronization, so we
   // just use a LinkedList instead and pretend its a stack.

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetNode.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetNode.java?rev=1523719&r1=1523718&r2=1523719&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetNode.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetNode.java Mon Sep 16 16:08:41 2013
@@ -45,7 +45,8 @@ class SkinStyleSheetNode
     Set<Locale>                      locales,
     AgentAtRuleMatcher               agentMatcher,
     Set<String>                      accProperties,
-    int                              mode)
+    int                              mode,
+    String                           clientRule)
   {
     _skinSelectorNodeList = skinSelectorNodeList;
     _namespaceMap = namespaceMap;
@@ -55,6 +56,7 @@ class SkinStyleSheetNode
     _platforms     = null;
     _accProperties = accProperties;
     _mode = mode;
+    _clientRule = clientRule;
   }
 
   SkinStyleSheetNode(
@@ -64,7 +66,8 @@ class SkinStyleSheetNode
     AgentAtRuleMatcher         agentMatcher,
     int[]                      platforms,
     Set<String>                accProperties,
-    int                        mode)
+    int                        mode,
+    String                     clientRule)
   {
     _namespaceMap = namespaceMap;
     _direction = direction;
@@ -73,6 +76,7 @@ class SkinStyleSheetNode
     _platforms = platforms;
     _accProperties = accProperties;
     _mode = mode;
+    _clientRule = clientRule;
   }
 
   public void add(SkinSelectorPropertiesNode node)
@@ -132,6 +136,11 @@ class SkinStyleSheetNode
     return _mode;
   }
 
+  public String getClientRule()
+  {
+    return _clientRule;
+  }
+
   public Set<String> getAcessibilityProperties()
   {
     return _accProperties;
@@ -143,7 +152,8 @@ class SkinStyleSheetNode
     int[]                      platforms,
     Set<Locale>                locales,
     Set<String>                accProperties,
-    int                        mode)
+    int                        mode,
+    String                     clientRule)
   {
     if (direction == _direction)
     {
@@ -162,7 +172,12 @@ class SkinStyleSheetNode
             {
               boolean modeMatch = (mode == _mode);
               if (modeMatch)
-                return true;
+              {
+                boolean clientRuleMatch = _objectsEqual(clientRule, _clientRule);
+
+                if (clientRuleMatch)
+                  return true;
+              }
             }
           }
         }
@@ -198,4 +213,6 @@ class SkinStyleSheetNode
   private final Set<Locale> _locales;
   private final Set<String> _accProperties;
   private final int _mode;
+  private final String _clientRule; // client side rule to be rendered as is to the css, eg: @media, @document
+
 }

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetParserUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetParserUtils.java?rev=1523719&r1=1523718&r2=1523719&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetParserUtils.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinStyleSheetParserUtils.java Mon Sep 16 16:08:41 2013
@@ -194,7 +194,8 @@ class SkinStyleSheetParserUtils
     // loop through the selectors and its properties
     for (SkinStyleSheetNode skinSSNode : skinSSNodeList)
     {
-
+      // client rule such as @media, @keyframes
+      String clientRule = skinSSNode.getClientRule();
       // selector and its properties
       List <SkinSelectorPropertiesNode> selectorNodeList =
         skinSSNode.getSelectorNodeList();
@@ -246,12 +247,13 @@ class SkinStyleSheetParserUtils
           // create an IconNode object and add it to the iconNodeList
           // This method returns hasContentProperty=false if there isn't a content attribute.
           boolean hasContentProperty = _addIconNode(sourceName,
-                                              baseSourceURI,
-                                              selectorName,
-                                              noTrPropertyList,
-                                              resolvedProperties.getTrRuleRefList(),
-                                              resolvedProperties.getInhibitedProperties(),    
-                                              iconNodeList);
+                                                    baseSourceURI,
+                                                    selectorName,
+                                                    clientRule,
+                                                    noTrPropertyList,
+                                                    resolvedProperties.getTrRuleRefList(),
+                                                    resolvedProperties.getInhibitedProperties(),
+                                                    iconNodeList);
 
           if (!hasContentProperty)
           {
@@ -266,6 +268,7 @@ class SkinStyleSheetParserUtils
                 _LOG.warning("SELECTOR_SHOULD_NOT_END_IN_ICON", selectorName);
             }
             _addStyleNode(selectorName,
+                          clientRule,
                           noTrPropertyList,
                           trSkinPropertyNodeList,
                           resolvedProperties.getTrRuleRefList(),
@@ -280,6 +283,7 @@ class SkinStyleSheetParserUtils
         {
 
           _addStyleNode(selectorName,
+                        clientRule,
                         noTrPropertyList,
                         trSkinPropertyNodeList,
                         resolvedProperties.getTrRuleRefList(),
@@ -561,6 +565,7 @@ class SkinStyleSheetParserUtils
     String             sourceName,
     String             baseSourceURI,
     String             selectorName,
+    String             clientRule,
     List<PropertyNode> noTrPropertyNodeList,
     List<String>       trRuleRefList,
     Set<String>        inhibitedProperties,
@@ -693,6 +698,7 @@ class SkinStyleSheetParserUtils
       StyleNode styleNode =
         new StyleNode(null, // name
                       selectorName,
+                      clientRule,
                       propertyNodeArray,
                       null, //TODO jmw trSkinPropertyNodes for icons
                       includeStyleNodes.toArray(new IncludeStyleNode[0]),
@@ -731,6 +737,7 @@ class SkinStyleSheetParserUtils
    */
   private static void _addStyleNode(
     String                    selectorName,
+    String                    clientRule,
     List<PropertyNode>        propertyNodeList,
     List<PropertyNode>        skinPropertyNodeList,
     List<String>              trRuleRefList,
@@ -741,7 +748,10 @@ class SkinStyleSheetParserUtils
     List<StyleNode>           styleNodeList)
   {
 
-    StyleNode styleNode = _createStyleNode(selectorName, propertyNodeList, skinPropertyNodeList,
+    StyleNode styleNode = _createStyleNode(selectorName,
+                                           clientRule,
+                                           propertyNodeList,
+                                           skinPropertyNodeList,
                                            trRuleRefList, 
                                            includePropertyNodes,
                                            embeddedIncludePropertyNodes,
@@ -754,6 +764,7 @@ class SkinStyleSheetParserUtils
 
   private static StyleNode _createStyleNode(
     String                    selectorName,
+    String                    clientRule,
     List<PropertyNode>        propertyNodeList,
     List<PropertyNode>        skinPropertyNodeList,
     List<String>              trRuleRefList,
@@ -816,6 +827,7 @@ class SkinStyleSheetParserUtils
     StyleNode styleNode =
       new StyleNode(name,
                     selector,
+                    clientRule,
                     propertyArray,
                     skinPropertyNodeList.isEmpty() ?
                       null : skinPropertyNodeList.toArray(new PropertyNode[0]),

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=1523719&r1=1523718&r2=1523719&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 Mon Sep 16 16:08:41 2013
@@ -20,8 +20,8 @@ package org.apache.myfaces.trinidadinter
 
 import java.beans.Beans;
 
-
 import java.io.PrintWriter;
+import java.io.StringWriter;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -54,9 +54,10 @@ 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.
+   * Returns a MatchingStyles object of normalized propertyString to the StyleNodes that have them as properties.
+   * MatchingStyles segregates the clientRules from the normal selectors for ease of css rendering.
+   * MatchingStyles object uses LinkedHashMap within, to indicate that relative 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.
@@ -64,14 +65,12 @@ public class CSSGenerationUtils
    * @param styleNodes
    * @return
    */
-  private static LinkedHashMap<String, List<StyleNode>> _buildMatchingStylesMap(List<StyleNode> styleNodes)
+  private static MatchingStyles _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);
-    
+    MatchingStyles matchingStyles = new MatchingStyles(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.
@@ -82,28 +81,23 @@ public class CSSGenerationUtils
         // 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))
+        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);
+          if (styleNode.hasClientRule())
+          {
+            matchingStyles.addStyle(styleNode.getClientRule(), propertyString, styleNode);
+          }
+          else
+          {
+            matchingStyles.addStyle(propertyString, styleNode);
           }
-          
-          matchingStyles.add(styleNode);
         }
       }
     }
     
-    return matchingStylesMap;
+    return matchingStyles;
   }
-  
+
   /**
    * Writes the properties of a merged set of style rules
    * @param out
@@ -259,14 +253,14 @@ public class CSSGenerationUtils
   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
@@ -275,7 +269,7 @@ public class CSSGenerationUtils
     else
     {
       _writeValidSelectors(out, validSelectors);
-    
+
       return selectorsToWrite;
     }
   }
@@ -435,47 +429,51 @@ 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.
 
+    // This is the first pass where MatchingStyles object is created from
+    // the styleNodes. MatchingStyles object segregates the normal selectors
+    // rendered as selector {prop: value} and clientRule selectors which are
+    // rendered as clientRule { selector { prop: value }}
+    // The segregation of clientRules allow us to segregate the rendering logic.
+    MatchingStyles matchingStyles = _buildMatchingStylesMap(styleNodes);
 
     // 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);
 
-    LinkedHashMap<String, List<StyleNode>> matchingStylesMap = _buildMatchingStylesMap(styleNodes);
-
-    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
+    // 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())
+    int fileSelectorsWritten = 0;
+
+    // read out the normal selectors, we render these first
+    LinkedHashMap<String, List<StyleNode>> noClientRuleMap = matchingStyles.getNonClientRuleMap();
+    Iterator<Map.Entry<String, List<StyleNode>>> noClientRuleEntries = noClientRuleMap.entrySet().iterator();
+
+    PrintWriter out = writerFactory.createWriter();
+
+    if (out == null)
     {
-      PrintWriter out = writerFactory.createWriter();
-      
-      if (out == null)
-      {
-        return;
-      }
- 
-      if (!compressStyles)
-      {
-        Date date = new Date();
+      return;
+    }
 
-        // write out the header with a time stamp
-        out.println("/* This CSS file generated on " + date + " */");
-      }
+    _beginCssFile(out, compressStyles);
+
+    // loop over all of the entries
+    while (noClientRuleEntries.hasNext())
+    {
 
       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())
+      while (abortedEntry == null && noClientRuleEntries.hasNext())
       {
         // get the entry to write out.  This handles retrying any aborted entry
         Map.Entry<String, List<StyleNode>> currEntry;
-        
+
+        // aborted entry is set when the max number of selectors have already
+        // written out for the current css file. if aborted entry is set then
+        // a new file needs to be created to write remaining selectors
         if (abortedEntry != null)
         {
           currEntry = abortedEntry;
@@ -483,7 +481,7 @@ public class CSSGenerationUtils
         }
         else
         {
-          currEntry = mergedEntries.next();
+          currEntry = noClientRuleEntries.next();
         }
 
         // write the entry
@@ -494,29 +492,227 @@ public class CSSGenerationUtils
         // detect abort
         if (entrySelectorsWritten == 0)
         {
+          // no selectors written, so we have reached the threshold for this css file
+          // set the aborted entry so that we will create a new css file
           abortedEntry = currEntry;
         }
         else
         {
+          // add up the selectors written to track the threshold for the css file
           fileSelectorsWritten += entrySelectorsWritten;
         }
       }
-      
-      if (!compressStyles)
+
+      if (abortedEntry != null)
       {
-        out.print("/* The number of CSS selectors in this file is ");
-        out.print(fileSelectorsWritten);
-        out.println(" */");
+        // abort detected, close the current out and create a new one
+        _endCssFile(out, styleSheetName, compressStyles, fileSelectorsWritten);
+        fileSelectorsWritten = 0;
+
+        // create new out
+        out = writerFactory.createWriter();
+
+        if (out == null)
+        {
+          return;
+        }
+
+        // put in the headers
+        _beginCssFile(out, compressStyles);
       }
+    }
+
+    Set<String> clientRules = matchingStyles.getClientRules();
+
+    // while writing a client rule, all selectors within the clien rule has to fit into the current css file.
+    // If that cannot happen, then we need to create a new css file and continue writing.
+    for (String clientRule : clientRules)
+    {
+      // write out all client rule entries into a temp out, so that we will know if it can be fitted in
+      // the current css file
+      StringWriter tempStringWriter = new StringWriter();
+      PrintWriter tempWriter = new PrintWriter(tempStringWriter);
       
-      if (out.checkError())
+      // we cannot count the selectors within the client rule to decide the number of selectors
+      // there are cases where both compressed and non-compressed selectors get written
+      // so we create a temp StringWriter and get the selectors and properties written into that
+      // we check if all selectors enclosed in a particular client rule can be incorporated in the current out
+      // otherwise we create a new out and write out the client rule followed by the selectors within
+      int clientRuleSelectorsWritten = _writeClientRuleStyles(tempWriter, clientRule, matchingStyles, maxSelectorsPerFile,
+                                                              fileSelectorsWritten, styleSheetName, baseURI, compressStyles,
+                                                              shortStyleClassMap,namespacePrefixArray, afSelectorMap);
+
+      // if no selectors were written, then we need a new file to contain the contents of the current clientRule
+      if (clientRuleSelectorsWritten == 0)
+      {
+        // all selectors inside client rule cannot be accommodated in current file
+        _endCssFile(out, styleSheetName, compressStyles, fileSelectorsWritten);
+        fileSelectorsWritten = 0;
+
+        // create new out
+        out = writerFactory.createWriter();
+
+        if (out == null)
+        {
+          return;
+        }
+
+        // write headers
+        _beginCssFile(out, compressStyles);
+
+        // close the tempWriter and create a new one
+        // some selectors inside the client rule may have got written to tempWriter
+        tempWriter.close();
+        tempStringWriter = new StringWriter();
+        tempWriter = new PrintWriter(tempStringWriter);
+
+        // now that we recreated the writer, so it should have space to write all the selectors in the client rule
+        clientRuleSelectorsWritten = _writeClientRuleStyles(tempWriter, clientRule, matchingStyles, 
+                                                            maxSelectorsPerFile, fileSelectorsWritten, styleSheetName, 
+                                                            baseURI, compressStyles, shortStyleClassMap,
+                                                            namespacePrefixArray, afSelectorMap);
+      }
+
+      // all selectors inside the current client rule should now be written to tempWriter
+      assert (clientRuleSelectorsWritten != 0);
+
+      if (tempWriter.checkError())
       {
-        _LOG.severe("Error writing stylehseet:" + styleSheetName);
+        // check for errors in tempWriter and log it and return
+        _LOG.severe("Error writing stylesheet:" + styleSheetName);
+        return;
       }
+
+      // the client rule can now be written to the actual out
+      out.print(clientRule);
+      _writeString(out, " {", compressStyles);
+
+      // write the selectors and properties to the actual out
+      out.print(tempStringWriter.toString());
+
+      // closing braces for client rule
+      _writeString(out, "}", compressStyles);
+
+      fileSelectorsWritten += clientRuleSelectorsWritten;
+
+      tempWriter.close();
+    }
+
+    _endCssFile(out, styleSheetName, compressStyles, fileSelectorsWritten);
+  }
+
+  /**
+   * tests if all the selectors under the client rule can fit into the current css file.
+   * writes the selectors and properties into the PrintWriter passed.
+   * 
+   * @param out
+   * @param clientRule
+   * @param matchingStyles
+   * @param maxSelectorsPerFile
+   * @param fileSelectorsWritten
+   * @param styleSheetName
+   * @param baseURI
+   * @param compressStyles
+   * @param shortStyleClassMap
+   * @param namespacePrefixArray
+   * @param afSelectorMap
+   * @return number of selectors written into the css file. Returns 0, if all selectors does not fit
+   *
+   */
+  private static int _writeClientRuleStyles(PrintWriter out, 
+                                            String clientRule, 
+                                            MatchingStyles matchingStyles, 
+                                            int maxSelectorsPerFile,
+                                            int fileSelectorsWritten, 
+                                            String styleSheetName, 
+                                            String baseURI,
+                                            boolean compressStyles, 
+                                            Map<String, String> shortStyleClassMap,
+                                            String[] namespacePrefixArray,
+                                            Map<String, String> afSelectorMap)
+  {
+    int selectorsWritten = 0;
+    int entrySelectorsWritten = 0;
+    
+    // we know this cannot be null because the caller is using the client rule keySet from matchingStyles
+    LinkedHashMap<String, List<StyleNode>> clientRuleMap = matchingStyles.getClientRuleMap(clientRule);
+    Iterator<Map.Entry<String, List<StyleNode>>> clientRuleEntries = clientRuleMap.entrySet().iterator();
+    
+    while (clientRuleEntries.hasNext())
+    {
+      Map.Entry<String, List<StyleNode>> currEntry = clientRuleEntries.next();
+      int selectorsLeft = maxSelectorsPerFile - (fileSelectorsWritten + selectorsWritten);
+      entrySelectorsWritten = _writeMergedEntry(styleSheetName, baseURI, compressStyles, shortStyleClassMap,
+                                                namespacePrefixArray, afSelectorMap, out, selectorsLeft,
+                                                currEntry);
       
-      // close this PrintWriter
-      out.close();
+      // we want to write all selectors in clientRuleEntries into the same css file
+      // so, if we cannot write any one of these selector we need to abort
+      if (entrySelectorsWritten == 0)
+      {
+        return 0;
+      }
+      else
+      {
+        selectorsWritten += entrySelectorsWritten;
+      }
     }
+
+    return selectorsWritten;
+  }
+
+  /**
+   * outputs header for a css file
+   * @param out
+   * @param compressStyles
+   */
+  private static void _beginCssFile(PrintWriter out, boolean compressStyles)
+  {
+    if (!compressStyles)
+    {
+      Date date = new Date();
+
+      // write out the header with a time stamp
+      out.println("/* This CSS file generated on " + date + " */");
+    }
+  }
+
+  /**
+   * outputs the footer for a css file
+   * @param out
+   * @param styleSheetName
+   * @param compressStyles
+   * @param fileSelectorsWritten
+   */
+  private static void _endCssFile(PrintWriter out, String styleSheetName, boolean compressStyles, int fileSelectorsWritten)
+  {
+    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 stylesheet:" + styleSheetName);
+    }
+
+    out.close();
+  }
+
+  /**
+   * Util method to output a outStr based on compressStyles
+   * @param out
+   * @param outStr
+   * @param compressStyles
+   */
+  private static void _writeString(PrintWriter out, String outStr, boolean compressStyles)
+  {
+    if (compressStyles)
+      out.print(outStr); // take out the newlines for performance
+    else
+      out.println(outStr);
   }
 
   /**
@@ -1421,7 +1617,7 @@ public class CSSGenerationUtils
         {
           // if we are in a pseudo-class already, and we get a ':' or '.' that means
           // this pseudo-class is complete. Get ready for another one.
-          String convertedPseudoClass = _convertPseudoClass(pseudoClassBuffer.toString(), afNamespacedSelector);
+          String convertedPseudoClass = _convertPseudoClass(completeSelector, pseudoClassBuffer.toString(), afNamespacedSelector);
           completeBuffer.append(convertedPseudoClass);
           pseudoClassBuffer = new StringBuffer();
           inPseudoClass = false;
@@ -1448,7 +1644,7 @@ public class CSSGenerationUtils
     }
     if (inPseudoClass)
     {
-      String mappedPseudoClass = _convertPseudoClass(pseudoClassBuffer.toString(), afNamespacedSelector);
+      String mappedPseudoClass = _convertPseudoClass(completeSelector, pseudoClassBuffer.toString(), afNamespacedSelector);
       completeBuffer.append(mappedPseudoClass);
 
     }
@@ -1536,7 +1732,7 @@ public class CSSGenerationUtils
    * @param afNamespacedSelector true if the pseudoClass is part of AF namespaced selector
    * @return
    */
-  static private String _convertPseudoClass(String pseudoClass, boolean afNamespacedSelector)
+  static private String _convertPseudoClass(String completeSelector, String pseudoClass, boolean afNamespacedSelector)
   {
     // The design time needs the browser-supported pseudo-classes to be converted so they
     // can show a preview of the skinned component.
@@ -1554,6 +1750,10 @@ public class CSSGenerationUtils
     if (_BUILT_IN_PSEUDO_CLASSES.contains(builtInPseudoClass) && !Beans.isDesignTime())
       return pseudoClass;
 
+    // skip the pseudo selectors in _AT_PAGE_PSEUDO_CLASSES only for @page client rule
+    if (completeSelector.contains(_AT_PAGE_SELECTOR) && _AT_PAGE_PSEUDO_CLASSES.contains(builtInPseudoClass))
+      return pseudoClass;
+
     // _BACKWARD_COMPATIBLE_CSS3_PSEUDO_CLASSES is treated differently
     // for namespaced selectors we render it prefixed with "p_AF"
     // for non-namespaced selectors we render it directly
@@ -1576,12 +1776,128 @@ public class CSSGenerationUtils
     return builder.toString();
   }
 
+  /**
+   * encapsulates the propertyString vs StyleNode maps separately for clientRule and normal selectors
+   * ClientRule selectors the styles are rendered as:
+   * clientRule { selector: {property1: value1, property2: value2 ... } }
+   * Normal selectors are rendered as
+   * selector: {property1: value1 ... }
+   */
+  private static final class MatchingStyles
+  {
+    private MatchingStyles(int styleCount)
+    {
+      this._nonClientRuleEntry = new MatchingEntry(styleCount);
+      this._clientRuleEntries = new LinkedHashMap<String, MatchingEntry>();
+    }
+
+
+    public LinkedHashMap<String, List<StyleNode>> getNonClientRuleMap()
+    {
+      return _nonClientRuleEntry.getMatchingStyles();
+    }
+
+    public LinkedHashMap<String, List<StyleNode>> getClientRuleMap(String clientRule)
+    {
+      return _clientRuleEntries.get(clientRule).getMatchingStyles();
+    }
+
+    public Set<String> getClientRules()
+    {
+      return _clientRuleEntries.keySet();
+    }
+
+    public void addStyle(String propertyString, StyleNode styleNode)
+    {
+      _nonClientRuleEntry.addMatchingStyle(propertyString, styleNode);
+    }
+
+    public void addStyle(String clientRule, String propertyString, StyleNode styleNode)
+    {
+      MatchingEntry matchingStylesForClientRule = _clientRuleEntries.get(clientRule);
+
+      if (matchingStylesForClientRule == null)
+      {
+        matchingStylesForClientRule = new MatchingEntry(1);
+        _clientRuleEntries.put(clientRule, matchingStylesForClientRule);
+      }
+
+      matchingStylesForClientRule.addMatchingStyle(propertyString, styleNode);
+    }
+
+    // matching styles map with no client rules
+    private final MatchingEntry _nonClientRuleEntry;
+
+    // matching styles map with client rules
+    private final LinkedHashMap<String, MatchingEntry> _clientRuleEntries;
+  }
+
+  /**
+   * encapsulates propertyString vs StyleNode map
+   * manages addition of StyleNode for a matching propertyString
+   */
+  private final static class MatchingEntry
+  {
+    public MatchingEntry(int initialSize)
+    {
+      _matchingStyles = new LinkedHashMap<String, List<StyleNode>>(initialSize);
+    }
+
+    public void addMatchingStyle(String propertyString, StyleNode styleNode)
+    {
+      if (propertyString == null || propertyString.equals(""))
+        return;
+
+
+      // See if we already have a StyleNode with the same properties
+      List<StyleNode> matchingStyles = _matchingStyles.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);
+        _matchingStyles.put(propertyString, matchingStyles);
+      }
+
+      matchingStyles.add(styleNode);
+    }
+
+    public LinkedHashMap<String, List<StyleNode>> getMatchingStyles()
+    {
+      return _matchingStyles;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      MatchingEntry that = (MatchingEntry) o;
+
+      if (_matchingStyles != null ? !_matchingStyles.equals(that._matchingStyles) : that._matchingStyles != null)
+        return false;
+
+      return true;
+    }
+
+    @Override
+    public int hashCode()
+    {
+      return _matchingStyles != null ? _matchingStyles.hashCode() : 0;
+    }
+
+    private LinkedHashMap<String, List<StyleNode>> _matchingStyles;
+  }
+
   // We want to output to the css the browser-supported pseudo-classes as is
   static private final Set<String> _BUILT_IN_PSEUDO_CLASSES = new HashSet<String>();
   static private final Set<String> _BACKWARD_COMPATIBLE_CSS3_PSEUDO_CLASSES = new HashSet<String>();
 
   // We want to output to the css the browser-supported pseudo-elements (HTML5/ CSS3) as is
   static private final Set<String> _BUILT_IN_PSEUDO_ELEMENTS = new HashSet<String>();
+  static private final Set<String> _AT_PAGE_PSEUDO_CLASSES = new HashSet<String>();
+
   static
   {
     /** CSS 2 pseudo classes */
@@ -1634,12 +1950,18 @@ public class CSSGenerationUtils
     _BUILT_IN_PSEUDO_ELEMENTS.add("::line-marker");
     _BUILT_IN_PSEUDO_ELEMENTS.add("::selection");
     _BUILT_IN_PSEUDO_ELEMENTS.add("::-webkit-input-placeholder");
+
+    /** @page pseudo classes*/
+    _AT_PAGE_PSEUDO_CLASSES.add(":first");
+    _AT_PAGE_PSEUDO_CLASSES.add(":left");
+    _AT_PAGE_PSEUDO_CLASSES.add(":right");
   }
   
   private static final Pattern _DASH_PATTERN =  Pattern.compile("-");
   private static final int _MSIE_SELECTOR_LIMIT = 4095;
   private static final String _DEFAULT_NAMESPACE = "af|";
   private static final String _DEFAULT_AF_SELECTOR = ".AF";
+  private static final String _AT_PAGE_SELECTOR = "@page";
 
   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(CSSGenerationUtils.class);
 }

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleNode.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleNode.java?rev=1523719&r1=1523718&r2=1523719&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleNode.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleNode.java Mon Sep 16 16:08:41 2013
@@ -41,6 +41,7 @@ public class StyleNode
   public StyleNode(
     String                 name,
     String                 selector,
+    String                 clientRule,
     PropertyNode[]         properties,
     PropertyNode[]         skinProperties,
     IncludeStyleNode[]     includedStyles,
@@ -51,6 +52,7 @@ public class StyleNode
   {
     this(name,
          selector,
+         clientRule,
          properties,
          skinProperties,
          includedStyles,
@@ -66,6 +68,7 @@ public class StyleNode
   public StyleNode(
     String                 name,
     String                 selector,
+    String                 clientRule,
     PropertyNode[]         properties,
     PropertyNode[]         skinProperties,
     IncludeStyleNode[]     includedStyles,
@@ -79,6 +82,7 @@ public class StyleNode
     // ---------------------------------------------
     _name = name;
     _selector = selector;
+    _clientRule = clientRule;
     _resetProperties = resetProperties;
 
 
@@ -203,6 +207,23 @@ public class StyleNode
   }
 
   /**
+   * @return clientRule
+   */
+  public String getClientRule()
+  {
+    return _clientRule;
+  }
+
+  /**
+   * util method to determine is a StyleNode has clientRule or not
+   * @return true if clientRule exists
+   */
+  public boolean hasClientRule()
+  {
+    return _clientRule != null && !_clientRule.isEmpty();
+  }
+
+  /**
    * Returns true if the style node has no properties. 
    */
   public boolean isEmpty()
@@ -306,6 +327,7 @@ public class StyleNode
     return
       (_name == test._name || (_name != null && _name.equals(test._name))) &&
       (_selector == test._selector || (_selector != null && _selector.equals(test._selector))) &&
+      (_clientRule == test._clientRule || (_clientRule != null && _clientRule.equals(test._clientRule))) &&
       (_resetProperties == test._resetProperties) &&
       (_inhibitAll == test._inhibitAll) &&
       (_inhibitedProperties.equals(test._inhibitedProperties)) &&
@@ -322,6 +344,7 @@ public class StyleNode
     int hash = 17;
     hash = 37*hash + ((null == _name) ? 0 : _name.hashCode());
     hash = 37*hash + ((null == _selector) ? 0 : _selector.hashCode());
+    hash = 37*hash + ((null == _clientRule) ? 0 : _clientRule.hashCode());
     hash = 37*hash + (_resetProperties ? 0 : 1);
     hash = 37*hash + (_inhibitAll ? 0 : 1);
     hash = 37*hash + _inhibitedProperties.hashCode();
@@ -340,6 +363,7 @@ public class StyleNode
     return getClass().getName() + "[" +
       "name="   + _name   + ", " +
       "selector=" + _selector + ", " +
+      "clientRule=" + _clientRule + ", " +
       "properties="  + _properties.toString()  + ", " +
       "skinProperties="  + _skinProperties.toString()  + ", " +
       "includeStyles="  + _includedStyles.toString()  + ", " +
@@ -359,8 +383,38 @@ public class StyleNode
     return _resetProperties;
   }
 
+  /**
+   * utility method to generate an id for a StyleNode.
+   * This is used in StyleSheetDocument object while resolving StyleNodes.
+   * This helps in distinguishing same selectors wrapped in different clientRules.
+   *
+   * @return id - clientRule if exists + selector or name whichever exist.
+   */
+  String getId()
+  {
+    StringBuilder id = new StringBuilder();
+
+    if (hasClientRule())
+    {
+      id.append(_clientRule);
+    }
+
+    // either one of _selector or _name should exist
+    if (_selector != null && !_selector.isEmpty())
+    {
+      id.append(_selector);
+    }
+    else if (_name != null && !_name.isEmpty())
+    {
+      id.append(_name);
+    }
+
+    return id.toString();
+  }
+
   private final String                     _name;
   private final String                     _selector;
+  private final String                     _clientRule;          // client side rules such as @media
   private final List<PropertyNode>         _properties;          // The property nodes
   private final List<PropertyNode>         _skinProperties;      // The skin property nodes
   private final List<IncludeStyleNode>     _includedStyles;      // Included styles



Mime
View raw message