You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by an...@apache.org on 2012/03/21 20:33:47 UTC

svn commit: r1303525 [2/3] - in /myfaces/trinidad/trunk: src/site/xdoc/devguide/ trinidad-api/src/main/java/org/apache/myfaces/trinidad/context/ trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ trinidad-api/src/main/xrts/org/apache/myfaces/...

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/share/nls/NullLocaleContext.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/share/nls/NullLocaleContext.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/share/nls/NullLocaleContext.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/share/nls/NullLocaleContext.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.share.nls;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.TimeZone;
+
+import org.apache.myfaces.trinidad.context.LocaleContext;
+
+/**
+ * LocaleContext implementation for cases where the Locale is 
+ * null/unknown.
+ */
+public final class NullLocaleContext extends LocaleContext
+{
+  public static LocaleContext getLeftToRightContext()
+  {
+    return _LTR_INSTANCE;      
+  }
+
+  public static LocaleContext getRightToLeftContext()
+  {
+    return _RTL_INSTANCE;
+  }
+
+  @Override
+  public Locale getFormattingLocale()
+  {
+    return null;
+  }
+
+  @Override
+  public String getFormattingIANALocaleString()
+  {
+    return null;
+  }
+
+  @Override
+  public Locale getTranslationLocale()
+  {
+    return null;
+  }
+
+  @Override
+  public String getTranslationIANALocaleString()
+  {
+    return null;
+  }
+
+  @Override
+  public boolean isRightToLeft()
+  {
+    return _rightToLeft;
+  }
+
+  @Override
+  public TimeZone getTimeZone()
+  {
+    return null;
+  }
+
+  @Override
+  public ResourceBundle getBundle(String baseBundleName)
+  {
+    return null;
+  }
+
+  @Override
+  public int getTwoDigitYearStart()
+  {
+    return 0;
+  }
+
+  @Override
+  public char getGroupingSeparator()
+  {
+    return 0;
+  }
+
+  @Override
+  public char getDecimalSeparator()
+  {
+    return 0;
+  }
+  
+  private NullLocaleContext(boolean rightToLeft)
+  {
+    _rightToLeft = rightToLeft;
+  }
+  
+  private final boolean _rightToLeft;
+  
+  private static final LocaleContext _RTL_INSTANCE = new NullLocaleContext(true);
+  private static final LocaleContext _LTR_INSTANCE = new NullLocaleContext(false);
+}

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/AgentAtRuleMatcher.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/AgentAtRuleMatcher.java?rev=1303525&r1=1303524&r2=1303525&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/AgentAtRuleMatcher.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/AgentAtRuleMatcher.java Wed Mar 21 19:33:45 2012
@@ -20,6 +20,7 @@ package org.apache.myfaces.trinidadinter
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -31,10 +32,13 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.myfaces.trinidad.context.Version;
+import org.apache.myfaces.trinidad.util.Range;
+
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent;
 import org.apache.myfaces.trinidadinternal.style.util.NameUtils;
 
+
 /**
  * Threadsafe immutable class that stores the @agent rule for a particular @agent query string
  * from the skinning css file (e.g., @agent ie and (version:5), ie and (version:6), gecko {}).
@@ -44,7 +48,6 @@ import org.apache.myfaces.trinidadintern
  */
 public final class AgentAtRuleMatcher
 {
-
   /**
    * Enumeration representing the result of a call to <code>match</code>.
    * @see #match
@@ -219,6 +222,69 @@ public final class AgentAtRuleMatcher
   }
 
   /**
+   * Returns a non-null Collection of agent applications that are matched by 
+   * this matcher.
+   */
+  public Collection<TrinidadAgent.Application> getAllApplications()
+  {
+    return new ArrayList<TrinidadAgent.Application>(_selectorAgents.keySet());
+  }
+
+  /**
+   * Returns a non-null collection of agent version ranges that are matched by
+   * this matcher.
+   * 
+   * @param application the agent application for which matching version ranges
+   *   should be returned.
+   */
+  public Collection<Range<Version>> getAllVersionsForApplication(TrinidadAgent.Application application)
+  {
+    Collection<Range<Version>> versionRanges = new HashSet<Range<Version>>();
+    Collection<AgentMatcher> agentMatchers = _selectorAgents.get(application);
+    
+    for (AgentMatcher agentMatcher : agentMatchers)
+    {
+      Range<Version> versionRange = agentMatcher.getMatchedVersions();
+      assert(versionRange != null);
+
+      versionRanges.add(versionRange);
+    }
+    
+    return versionRanges;
+  }
+
+  /**
+   * Returns a non-null range of versions that represent that intersection
+   * of all verions that:
+   * 
+   * a) are matched by this matcher, and...
+   * b) contain the specified agent version.
+   * 
+   * @param application the agent application for which matching version ranges
+   *   should be returned.
+   * @param agentVersion only ranges that contain this version will be matched.
+   */  
+  public Range<Version> getMatchedVersionsForApplication(
+    TrinidadAgent.Application application,
+    Version                   agentVersion
+    )
+  {
+    Collection<Range<Version>> allVersions = getAllVersionsForApplication(application);
+    
+    Range<Version> matchedVersions = Version.ALL_VERSIONS;
+    
+    for (Range<Version> range : allVersions)
+    {
+      if (range.contains(agentVersion))
+      {
+        matchedVersions = matchedVersions.intersect(range);
+      }
+    }
+    
+    return matchedVersions;
+  }
+
+  /**
    * Parses touchScreen rule and creates appropriate AgentMatcher
    * @param currSelector
    */
@@ -490,6 +556,17 @@ public final class AgentAtRuleMatcher
      * @return <code>true</code> if the match succeeds
      */
     public abstract boolean match(TrinidadAgent agent);
+    
+    /**
+     * Returns the versions matched by this AgentMatcher.
+     * 
+     * By default, all versions are matched.  Subclasses should
+     * override to constrain to the versions that they match.
+     */
+    public Range<Version> getMatchedVersions()
+    {
+      return Version.ALL_VERSIONS;
+    }
 
     @Override
     public abstract int hashCode();
@@ -556,6 +633,29 @@ public final class AgentAtRuleMatcher
     }
 
     @Override
+    public Range<Version> getMatchedVersions()
+    {
+      Version start = Version.MIN_VERSION;
+      Version end = Version.MAX_VERSION;
+      
+      switch (_comparison)
+      {
+        case MIN:
+          start = _version.toMinimumVersion();
+          break;
+        case MAX:
+          end = _version.toMaximumVersion();
+          break;
+        case EQUALS:
+          start = _version.toMinimumVersion();;
+          end = _version.toMaximumVersion();;
+          break;
+      }
+      
+      return Range.of(start, end);
+    }
+
+    @Override
     public final int hashCode()
     {
       return _hashCode;
@@ -613,23 +713,28 @@ public final class AgentAtRuleMatcher
       // their hash codes each time our hash code is called
       _hashCode = matchers.hashCode();
 
+      boolean hasVersionMatcher = false;
+      boolean hasTouchMatcher = false;
       for (AgentMatcher matcher : matchers)
       {
         if (matcher instanceof VersionMatcher)
           hasVersionMatcher = true;
         if (matcher instanceof TouchScreenCapabilityMatcher)
-          hasTouchScreenCapabilityMatcher = true;
+          hasTouchMatcher = true;
       }
+      
+      _hasVersionMatcher = hasVersionMatcher;
+      _hasTouchScreenCapabilityMatcher = hasTouchMatcher;
     }
 
     protected boolean hasTouchScreenCapabilityMatcher()
     {
-      return hasTouchScreenCapabilityMatcher;
+      return _hasTouchScreenCapabilityMatcher;
     }
 
     protected boolean hasVersionMatcher()
     {
-      return hasVersionMatcher;
+      return _hasVersionMatcher;
     }
 
     /**
@@ -654,6 +759,19 @@ public final class AgentAtRuleMatcher
     }
 
     @Override
+    public Range<Version> getMatchedVersions()
+    {
+      Range<Version> versionRange = Version.ALL_VERSIONS;
+        
+      for (AgentMatcher matcher : _matchers)
+      {
+        versionRange = versionRange.intersect(matcher.getMatchedVersions());
+      }
+        
+      return versionRange;
+    }
+
+    @Override
     public int hashCode()
     {
       return _hashCode;
@@ -683,11 +801,12 @@ public final class AgentAtRuleMatcher
       return super.toString() + ", matchers=" + _matchers;
     }
 
+
+
     private final List<AgentMatcher> _matchers;
     private final int _hashCode;
-    private boolean hasVersionMatcher;
-    private boolean hasTouchScreenCapabilityMatcher;
-
+    private final boolean _hasVersionMatcher;
+    private final boolean _hasTouchScreenCapabilityMatcher;
   }
 
   /**

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/AllVariantsSkinPregenerator.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/AllVariantsSkinPregenerator.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/AllVariantsSkinPregenerator.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/AllVariantsSkinPregenerator.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.skin.pregen;
+
+import java.util.HashSet;
+import java.util.List;
+
+import java.util.Set;
+
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+import org.apache.myfaces.trinidad.skin.Skin;
+
+import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent.Application;
+import org.apache.myfaces.trinidadinternal.skin.DocumentProviderSkin;
+import org.apache.myfaces.trinidadinternal.skin.pregen.config.PregenConfig;
+import org.apache.myfaces.trinidadinternal.skin.pregen.context.PregenStyleContext;
+import org.apache.myfaces.trinidadinternal.skin.pregen.variant.SkinVariant;
+import org.apache.myfaces.trinidadinternal.skin.pregen.variant.SkinVariants;
+import org.apache.myfaces.trinidadinternal.style.StyleContext;
+import org.apache.myfaces.trinidadinternal.style.StyleProvider;
+import org.apache.myfaces.trinidadinternal.style.util.NameUtils;
+import org.apache.myfaces.trinidadinternal.style.xml.parse.StyleSheetDocument;
+import org.apache.myfaces.trinidadinternal.style.xml.parse.StyleSheetNode;
+
+/**
+ * SkinPregenerator implementation that generates all possible style
+ * sheet variants for the skin.
+ * 
+ * This class is not thread safe.
+ */
+class AllVariantsSkinPregenerator implements SkinPregenerator
+{
+  @Override
+  public void pregenerate(
+    FacesContext    context, 
+    Skin            skin,
+    PregenConfig    config,
+    StyleProvider   provider
+    )
+  {
+    _pregenerateWithStats(context, skin, config, provider);
+  }
+ 
+  private void _pregenerateWithStats(
+    FacesContext   context,
+    Skin           skin,
+    PregenConfig   config,
+    StyleProvider  provider
+    )
+  {
+    Stats stats = new Stats(skin, config);
+    stats.start();
+    
+    try
+    {
+      _pregenerate(context, skin, config, provider, stats); 
+    }
+    finally
+    {
+      stats.end();
+    }
+  }
+  
+  private void _pregenerate(
+    FacesContext    context,
+    Skin            skin,
+    PregenConfig    config,
+    StyleProvider   provider,
+    Stats           stats
+    )
+  {
+    StyleSheetDocument document = _getDocumentForSkin(context, skin, config, provider);
+    _pregenerateAllVariants(context, config, provider, document, stats);     
+  }
+  
+  private StyleSheetDocument _getDocumentForSkin(
+    FacesContext  context,
+    Skin          skin,
+    PregenConfig  config,
+    StyleProvider provider
+    )
+  {
+    StyleSheetDocument document = null;
+    
+    if (skin instanceof DocumentProviderSkin)
+    {
+      StyleContext styleContext =
+        PregenStyleContext.documentContext(context, provider, config.getTargetDirectoryPath());
+      document = ((DocumentProviderSkin) skin).getStyleSheetDocument(styleContext);
+    }
+    
+    if (document == null)
+    {
+      _LOG.warning("SKIN_PREGEN_NO_DOCUMENT", skin.getId());
+      document = _EMPTY_DOCUMENT;
+    }
+        
+    return document;
+  }
+  
+  private void _pregenerateAllVariants(
+    FacesContext       context,
+    PregenConfig       config,    
+    StyleProvider      provider,
+    StyleSheetDocument document,
+    Stats              stats
+    )
+  {
+    SkinVariants variants = new SkinVariants(document, config);
+    
+    for (SkinVariant variant : variants)
+    {
+      _pregenerateVariant(context, config, provider, variant, stats);
+    }
+  }
+
+  private void _pregenerateVariant(
+    FacesContext           context,
+    PregenConfig           config,
+    StyleProvider          provider,
+    SkinVariant            variant,
+    Stats                  stats
+    )
+  {
+    StyleContext styleContext = _createStyleContext(context,
+                                                    config,
+                                                    provider,
+                                                    variant);
+
+    // This, finally, triggers pregeneration for the current variant.
+    List<String> uris = provider.getStyleSheetURIs(styleContext);
+    
+    stats.variant(variant, uris);
+    _variant = variant;
+  }
+
+  private StyleContext _createStyleContext(
+    FacesContext           context,
+    PregenConfig           config,
+    StyleProvider          provider,
+    SkinVariant            variant
+    )
+  {
+    assert(variant != null);
+    
+    return new PregenStyleContext(context, 
+                                  provider,
+                                  config.getTargetDirectoryPath(),
+                                  variant,
+                                  _isDirty(variant));
+  }
+
+  // Tests whether we should dirty the style provider when pregenerating
+  // the specified variant.  FileSystemStyleCache currently does not prune
+  // memory used by its entry caches.  During pregeneration, these caches
+  // become fully populated, which can put significant pressure on the heap.
+  // We keep memory usage in check by allowing the style provider to free
+  // up its caches from time to time.
+  private boolean _isDirty(SkinVariant variant)
+  {
+    // Switching between platforms seems like as good a time as any to let
+    // the style provider reset its caches.  This promotes cache reuse within
+    // each platform variant while not letting cache size get too out of control.
+    return ((_variant != null) && (_variant.getPlatform() != variant.getPlatform()));
+  }
+
+  // Little utility class for tracking statistics (eg.
+  // # of generated style sheets, duration, etc...)
+  private class Stats
+  {
+    public Stats(Skin skin, PregenConfig config)
+    {
+      _skin = skin;
+      _targetDirectoryPath = config.getTargetDirectoryPath();
+    }
+
+    public void start()
+    {
+      _logStartMessage();
+      _startTime = System.currentTimeMillis(); 
+    }
+
+    public void variant(SkinVariant variant, List<String> uris)
+    {
+      _visitedVariantsCount++;
+      
+      if (_isNewURI(uris))
+      {
+        _generatingVariantsCount++;
+        _uris.addAll(uris);
+      }
+      
+      _logVariant(variant);
+    }
+
+    public void end()
+    {
+      long endTime = System.currentTimeMillis();
+      _logEndMessage(endTime);
+    }
+
+    private boolean _isNewURI(List<String> uris)
+    {
+      return ((uris != null) && !uris.isEmpty() && !_uris.contains(uris.get(0)));
+    }
+    
+    private void _logStartMessage()
+    {
+      _LOG.info("SKIN_PREGEN_STARTING", _skin.getId());
+    }
+    
+    private void _logEndMessage(long endTime)
+    {
+      long duration = (endTime - _startTime);
+      
+      _LOG.info("SKIN_PREGEN_COMPLETED",
+                new Object[] {
+                  _skin.getId(),
+                  _uris.size(),
+                  _generatingVariantsCount,
+                  _visitedVariantsCount,
+                  duration,
+                  _targetDirectoryPath});
+    }
+    
+    private void _logVariant(SkinVariant variant)
+    {
+      assert(variant != null);
+      int platform = variant.getPlatform();
+      Application agentApplication = variant.getApplicationAndVersion().application;
+
+      if ((_variant == null)                   ||
+          (platform != _variant.getPlatform()) ||
+          (agentApplication != _variant.getApplicationAndVersion().application))
+      {
+        _LOG.info("SKIN_PREGEN_VARIANT",
+                  new Object[] {
+                    NameUtils.getPlatformName(platform),
+                    agentApplication.getAgentName(),
+                    _skin.getId() });
+      }
+    }
+
+    private final Skin        _skin;
+    private final String      _targetDirectoryPath;
+    private final Set<String> _uris = new HashSet<String>(101);
+    private int               _visitedVariantsCount = 0;
+    private int               _generatingVariantsCount = 0;
+    private long              _startTime = -1;    
+  }
+
+  // The variant that we are currently processing
+  private SkinVariant _variant;
+  
+  private static final StyleSheetDocument _EMPTY_DOCUMENT = 
+    new StyleSheetDocument(new StyleSheetNode[0], null, StyleSheetDocument.UNKNOWN_TIMESTAMP);
+
+  private static final TrinidadLogger _LOG =
+    TrinidadLogger.createTrinidadLogger(AllVariantsSkinPregenerator.class);
+}

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerationService.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerationService.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerationService.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerationService.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.trinidadinternal.skin.pregen;
+
+import java.io.IOException;
+
+import java.util.Map;
+
+import javax.faces.FacesException;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+import javax.faces.context.ResponseWriter;
+
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+import org.apache.myfaces.trinidad.render.InternalView;
+import org.apache.myfaces.trinidad.skin.Skin;
+import org.apache.myfaces.trinidad.skin.SkinFactory;
+import org.apache.myfaces.trinidad.util.Enums;
+import org.apache.myfaces.trinidadinternal.skin.pregen.config.InvalidConfigException;
+import org.apache.myfaces.trinidadinternal.skin.pregen.config.PregenConfig;
+
+/**
+ * InternalView implementation that provides skin pregeneration service.
+ *
+ * See the "Pre-generating Skin Style Sheets" section in the Skinning chapter
+ * of the Trinidad Developer's Guide for the specification of this service.
+ */
+public class SkinPregenerationService extends InternalView
+{ 
+  /**
+   * Tests whether the pregeneration service is enabled.
+   */
+  public static boolean isEnabled()
+  {
+    return (_getServiceStatus() == ServiceStatus.ON);
+  }
+
+  /**
+   * Tests whether the view id corresponds to a skin pregeneration request.
+   */
+  public static boolean isPregenerationRequest(String viewId)
+  {
+    return _VIEW_ID.equals(viewId);
+  }
+
+  @Override
+  public UIViewRoot createView(FacesContext context, String viewId)
+  {
+    return null;
+  }
+
+  @Override
+  public UIViewRoot restoreView(FacesContext context, String viewId)
+  {
+    return null;
+  }
+
+  @Override
+  public void renderView(FacesContext context, UIViewRoot viewToRender)
+    throws IOException, FacesException
+  {
+    if (!isEnabled())
+    {
+      _sendPregenerationDisabledError(context);
+      return;
+    }
+    
+    long duration = _pregenerateSkin(context);    
+    
+    if (!context.getResponseComplete())
+    {
+      _renderResponse(context, duration);
+    }
+  }
+  
+  // Pregenerates the skin based on the config specified via
+  // request parameters and returns the duration.
+  private static long _pregenerateSkin(FacesContext context)
+  {
+    long startTime = System.currentTimeMillis();
+
+    try
+    {
+      PregenConfig config = PregenConfig.parse(context);
+      Skin skin = _parseSkin(context);
+      SkinPregenerationUtils.pregenerate(context, skin, config);      
+    }
+    catch (Exception e)
+    {
+      _pregenFailed(context, e);
+    }
+
+    return (System.currentTimeMillis() - startTime);
+  }
+  
+  // Returns a non-null Skin corresponding based on the "id"
+  // request parameter.  Throws InvalidConfigException if no
+  // skin is found.
+  private static Skin _parseSkin(FacesContext context)
+    throws InvalidConfigException
+  {
+    String skinId = _getSkinId(context);
+    if (skinId != null)
+    {
+      Skin skin = _getSkin(context, skinId);
+      if (skin != null)
+      {
+        return skin;
+      }
+    }
+
+    String message = _LOG.getMessage("SKIN_PREGEN_REQUESTED_SKIN_INVALID", skinId);
+    throw new InvalidConfigException(message);
+  }
+  
+  private static String _getSkinId(FacesContext context)
+  {
+    ExternalContext external = context.getExternalContext();
+    return external.getRequestParameterMap().get(_SKIN_ID_REQUEST_PARAM);        
+  }
+  
+  private static Skin _getSkin(FacesContext context, String skinId)
+  {
+    SkinFactory factory = SkinFactory.getFactory();
+    return factory.getSkin(context, skinId);
+  }
+
+  private static void _pregenFailed(FacesContext context, Exception e)
+  {
+    String message = _LOG.getMessage("SKIN_PREGEN_FAILED", e.getMessage());
+    _LOG.severe(message, e);
+    _sendError(context, message);
+  }
+
+  private static void _sendPregenerationDisabledError(FacesContext context)
+  {
+    String message = _LOG.getMessage("SKIN_PREGEN_DISABLED",
+                                     new Object[] {_SERVICE_PROPERTY,
+                                                   _getServicePropertyValues()});
+
+    _sendError(context, message);
+  }
+  
+  private static String _getServicePropertyValues()
+  {
+    return Enums.patternOf(ServiceStatus.class,
+                           Enums.displayNameStringProducer(ServiceStatus.class));
+  }
+
+
+  private static void _sendError(FacesContext context, String message)
+  {
+    if (context.getResponseComplete())
+    {
+      return;
+    }
+
+    ExternalContext external = context.getExternalContext();
+    
+    try
+    {
+      external.responseSendError(500, message);
+    }
+    catch (IOException e)
+    {
+      _LOG.warning(e);
+    }
+    finally
+    {
+      context.responseComplete();
+    }
+  }
+
+  // Render the content to send back in the event that skin
+  // generation is successful.
+  private static void _renderResponse(
+    FacesContext context,
+    long         duration
+    ) throws IOException
+  {
+    ResponseWriter rw = _getResponseWriter(context);
+
+    rw.startDocument();
+    rw.write(_RESPONSE_DOCTYPE);
+    rw.startElement("html", null);
+    rw.startElement("head", null);
+    rw.startElement("title", null);
+    rw.writeText(_LOG.getMessage("SKIN_PREGEN_RESPONSE_TITLE"), null);
+    rw.endElement("title");
+    rw.endElement("head");
+    rw.startElement("body", null);
+    rw.startElement("p", null);
+
+    String message = _LOG.getMessage("SKIN_PREGEN_RESPONSE", duration);
+    rw.writeText(message, null);
+
+    rw.endElement("p");
+    rw.endElement("body");
+    rw.endElement("html");
+    rw.endDocument(); 
+  }
+  
+  private static ResponseWriter _getResponseWriter(FacesContext context)
+    throws IOException
+  {
+    ExternalContext external  = context.getExternalContext();
+    String contentType = "text/html";
+    String encoding = "UTF-8";
+
+    external.setResponseContentType(contentType);
+    external.setResponseCharacterEncoding(encoding);
+    
+    ResponseWriter rw = context.getRenderKit().createResponseWriter(
+      external.getResponseOutputWriter(),
+      contentType,
+      encoding);
+    
+    context.setResponseWriter(rw);
+    
+    return rw;
+  }
+  
+  private static ServiceStatus _getServiceStatus()
+  {
+    if (_sServiceStatus == null)
+    {   
+      String serviceProperty = System.getProperty(_SERVICE_PROPERTY);
+      if ((serviceProperty != null) && (serviceProperty.length() > 0))
+      {
+        try
+        {
+          _sServiceStatus = ServiceStatus.valueOfDisplayName(serviceProperty);
+        }
+        catch (IllegalArgumentException e)
+        {
+          _sServiceStatus = ServiceStatus.OFF;
+
+          _LOG.severe("ILLEGAL_SYSTEM_PROPERTY_VALUE",
+            new Object[] {
+              serviceProperty,
+              _SERVICE_PROPERTY,
+              _getServicePropertyValues()});
+        }
+      }
+    }
+    
+    return _sServiceStatus;
+  }
+  
+  // Little utility enum that we use to track the
+  // enabled/disabed status of the pregeneration service.
+  public static enum ServiceStatus
+  {
+    ON("on"),
+    OFF("off");
+    
+    ServiceStatus(String displayName)
+    {
+      _displayName = displayName;
+    }
+    
+    public String displayName()
+    {
+      return _displayName;
+    }
+
+    public static ServiceStatus valueOfDisplayName(String displayName)
+    {
+      return Enums.stringToEnum(_displayNameMap, displayName, ServiceStatus.class);
+    }
+    
+    private final String _displayName;
+    private static final Map<String, ServiceStatus> _displayNameMap;
+    
+    static
+    {
+      _displayNameMap = Enums.createDisplayNameMap(ServiceStatus.class);
+    }    
+  }
+
+  private static volatile ServiceStatus _sServiceStatus;
+  
+  private static final String _SKIN_ID_REQUEST_PARAM    = "id";
+
+  private static final String _SERVICE_PROPERTY =
+    "org.apache.myfaces.trinidad.SKIN_PREGENERATION_SERVICE";
+
+  private static final String _RESPONSE_DOCTYPE =
+    "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">";
+
+  // The view id for the pregeneration service.
+  private static final String _VIEW_ID = "/-tr-pregenerate-skins";
+
+  private static final TrinidadLogger _LOG =
+    TrinidadLogger.createTrinidadLogger(SkinPregenerationService.class);
+}

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerationUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerationUtils.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerationUtils.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerationUtils.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.skin.pregen;
+
+import java.beans.Beans;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.trinidad.skin.Skin;
+import org.apache.myfaces.trinidad.util.FileUtils;
+import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.TrinidadRenderingConstants;
+import org.apache.myfaces.trinidadinternal.skin.pregen.config.PregenConfig;
+import org.apache.myfaces.trinidadinternal.skin.pregen.context.PregenStyleProvider;
+import org.apache.myfaces.trinidadinternal.style.StyleProvider;
+
+/**
+ * Skin pregeneration utilities.
+ */
+public final class SkinPregenerationUtils
+{
+  /**
+   * Pregenerates style sheets for the specified skin.
+   *
+   * @param context the FacesContext for the current request
+   * @param skin the non-null Skin to pregenerate
+   * @param config configuration info that determines how pregeneration
+   *   is performed.
+   */
+  public static void pregenerate(
+    FacesContext    context,
+    Skin            skin,
+    PregenConfig    config
+    ) throws IOException
+  {
+    assert(skin != null);
+    assert(config != null);
+
+    SkinPregenerator pregenerator = _getSkinPregenerator();
+    String stylesCacheDirectoryPath = _getStylesCacheDirectoryPath(config);
+    StyleProvider provider = new PregenStyleProvider(skin, stylesCacheDirectoryPath);
+
+    pregenerator.pregenerate(context, skin, config, provider);
+  }
+  
+  private static SkinPregenerator _getSkinPregenerator()
+  {
+    if (Beans.isDesignTime())
+    {
+      return _NOOP_PREGENERATOR;
+    }
+
+    return new AllVariantsSkinPregenerator();
+  }
+
+  // Returns the subdirectory for skin-specific generated files.
+  // Throws an IOException if the directory does not exist/cannot be
+  // created or is not writable.
+  private static String _getStylesCacheDirectoryPath(PregenConfig config)
+    throws IOException
+  {
+    String targetDirPath = config.getTargetDirectoryPath();
+    String subDirPath = TrinidadRenderingConstants.STYLES_CACHE_DIRECTORY;
+    String stylesCacheDirPath = targetDirPath + subDirPath;
+    
+    // Verify that the directory exists and is writable.
+    FileUtils.toWritableDirectory(stylesCacheDirPath);
+    
+    return stylesCacheDirPath;
+  }
+
+  private SkinPregenerationUtils()
+  {
+  }
+  
+  private static final SkinPregenerator _NOOP_PREGENERATOR =
+    new SkinPregenerator()
+    {
+      @Override
+      public void pregenerate(
+        FacesContext    context,
+        Skin            skin, 
+        PregenConfig    config,
+        StyleProvider   provider)
+      {
+      }
+    };
+}

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerator.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerator.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerator.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/SkinPregenerator.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.skin.pregen;
+
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.trinidad.skin.Skin;
+import org.apache.myfaces.trinidadinternal.skin.pregen.config.PregenConfig;
+import org.apache.myfaces.trinidadinternal.style.StyleProvider;
+
+/**
+ * Interface for objects that perform skin pregeneration.
+ */
+interface SkinPregenerator
+{
+
+  /**
+   * Pregenerate style sheets for the specified skin
+   * @param context the FacesContext for the current request
+   * @param skin the Skin to pregenerate
+   * @param config configuration info that influences pregeneration
+   */
+  public void pregenerate(
+    FacesContext  context,
+    Skin          skin,
+    PregenConfig  config,
+    StyleProvider provider
+    );
+}

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/config/InvalidConfigException.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/config/InvalidConfigException.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/config/InvalidConfigException.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/config/InvalidConfigException.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.skin.pregen.config;
+
+/**
+ * Exception thrown from PregenConfig.parse() if a failure occurs during
+ * parsing.
+ */
+public final class InvalidConfigException extends RuntimeException
+{
+  /**
+   * Creates an InvalidConfigException with a message that is
+   * suitable for publication to the end user.
+   */
+  public InvalidConfigException(String message)
+  {
+    super(message);
+  }
+
+  private static final long serialVersionUID = 1L;
+}

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/config/PregenConfig.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/config/PregenConfig.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/config/PregenConfig.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/config/PregenConfig.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,623 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.skin.pregen.config;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.AbstractCollection;
+import java.util.Arrays;
+import java.util.Collection;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+import java.util.Map;
+
+import javax.faces.context.ExternalContext;
+
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.trinidad.context.AccessibilityProfile;
+import org.apache.myfaces.trinidad.context.LocaleContext;
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+import org.apache.myfaces.trinidad.util.EnumParseException;
+import org.apache.myfaces.trinidad.util.Enums;
+import org.apache.myfaces.trinidad.util.FileUtils;
+import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent;
+import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent.Application;
+import org.apache.myfaces.trinidadinternal.renderkit.core.CoreRenderingContext;
+import org.apache.myfaces.trinidadinternal.share.nls.NullLocaleContext;
+import org.apache.myfaces.trinidadinternal.util.nls.LocaleUtils;
+
+/**
+ * Specifies configuration constraints for skin pregeneration.
+ * 
+ * For the moment, only two types of configurations are supported:
+ * 
+ * - all: All possible variants are pregenerated
+ * - common: Only the most common variants are pregenerated
+ * 
+ * The "variants" request parameter specifies which of these two
+ * configurations to use.
+ * 
+ * In the future, we can consider supporting finer-grained configuration
+ * specification - eg. we could allow specific platforms/agents/locales
+ * to be specified via an XML configuration file.
+ */
+abstract public class PregenConfig
+{
+  /**
+   * Constant returned from variants accessors to indicate that
+   * all possible values of the variant should be included during
+   * pregeneration.
+   */
+  public static final Collection<?> ALL_VARIANTS =
+    new AbstractCollection() {
+    
+      @Override
+      public boolean isEmpty()
+      {
+        return false;
+      }
+  
+      @Override
+      public Iterator iterator()
+      {
+        throw new UnsupportedOperationException();
+      }
+
+      @Override
+      public int size()
+      {
+        throw new UnsupportedOperationException();
+      }
+    };
+
+  /**
+   * Returns a null/empty PregenConfig instance.
+   */
+  public static PregenConfig nullInstance()
+  {
+    return _NULL_CONFIG;    
+  }
+
+  /**
+   * Creates a PregenConfig instance that is configured based on request
+   * parmeter values.
+   * 
+   * Request parameters are documented in SkinPregenerationService class doc.
+   * 
+   * @param context the FacesContext to use for retreving request parameter
+   *   values.
+   * @return
+   */
+  public static PregenConfig parse(FacesContext context)
+    throws InvalidConfigException
+  {
+    ExternalContext external = context.getExternalContext();
+
+    return _isAllVariantsRequest(external) ?
+      new AllPregenConfig(context) :
+      new CommonPregenConfig(context);
+  }
+
+  private static boolean _isAllVariantsRequest(ExternalContext external)
+    throws InvalidConfigException
+  {
+    Collection<Variants> variants = _parseDisplayNameParam(external,
+                                                           "variants",
+                                                           Variants.class,
+                                                           Variants.COMMON);
+    
+    return variants.contains(Variants.ALL);
+  }
+
+  private static <E extends Enum> Collection<E> _parseDisplayNameParam(
+    ExternalContext external,
+    String          paramName,
+    Class<E>        enumClass, 
+    E               defaultValue
+    ) throws InvalidConfigException
+  {
+    try
+    {
+      return Enums.parseDisplayNameEnumRequestParameter(external,
+                                                        paramName,
+                                                        enumClass,
+                                                        defaultValue);
+    }
+    catch (EnumParseException e)
+    {
+      throw new InvalidConfigException(e.getMessage());
+    }
+  }
+
+  /**
+   * Returns the (non-null) platform variants to pregenerate.
+   * 
+   * The collection values correspond to TrinidadAgent.OS_* constants.
+   * 
+   * A return value of PregenConfig.ALL indicates that all platform
+   * variants should be pregenerated.
+   */
+  abstract public Collection<Integer> getPlatformVariants();
+  
+  /**
+   * Returns the (non-null) locale variants to pregenerate.
+   * 
+   * A return value of PregenConfig.ALL indicates that all locale
+   * variants should be pregenerated.
+   */
+  abstract public Collection<LocaleContext> getLocaleVariants();
+  
+  /**
+   * Returns the (non-null) reading direction variants to pregenerate.
+   * 
+   * A return value of PregenConfig.ALL indicates that both ltr and
+   * rtl variants should be pregenerated.
+   */
+  abstract public Collection<Integer> getReadingDirectionVariants();
+  
+  /**
+   * Returns the (non-null) agent application variants to pregenerate.
+   * 
+   * A return value of PregenConfig.ALL indicates that all agent application
+   * variants should be pregenerated.
+   */
+  abstract public Collection<Application> getAgentApplicationVariants();
+  
+  /**
+   * Returns the (non-null) accessibility variants to pregenerate.
+   * 
+   * A return value of PregenConfig.ALL indicates that all accessibility
+   * variants should be pregenerated.
+   */
+  abstract public Collection<AccessibilityProfile> getAccessibilityVariants();
+  
+  /**
+   * Returns the conatiner types to pregenerate.
+   */
+  abstract public Collection<ContainerType> getContainerTypes();
+
+  /**
+   * Returns the request types to pregenerate.
+   */
+  abstract public Collection<RequestType> getRequestTypes();
+  
+  /**
+   * Returns the style class types to pregenerate.
+   */
+  abstract public Collection<StyleClassType> getStyleClassTypes();
+  
+  /**
+   * Returns the path of the target output directory.
+   * 
+   * This returns a non-null path to an existing, writable directory.
+   * 
+   * The returned path is guaranteed to not have a trailing file
+   * separator.
+   */
+  abstract public String getTargetDirectoryPath();
+  
+  // Utility enum class used to identify whether PregenConfig.parse() should
+  // return a PregenConfig instance that is configured for generating
+  // "common" or "all" variants.
+  public enum Variants
+  {
+    COMMON("common"),
+    ALL("all");
+    
+    Variants(String displayName)
+    {
+      _displayName = displayName;
+    }
+    
+    public String displayName()
+    {
+      return _displayName;
+    }
+    
+    public static Variants valueOfDisplayName(String displayName)
+    {
+      return Enums.stringToEnum(_displayNameMap, displayName, Variants.class);
+    }
+    
+    private final String _displayName;
+    private static final Map<String, Variants> _displayNameMap;
+    
+    static
+    {
+      _displayNameMap = Enums.createDisplayNameMap(Variants.class);
+    }
+  }
+  
+  // Enum that is used to indicate whether pregeneration should target
+  // servlet or portlet containers (or both).
+  public enum ContainerType
+  {
+    SERVLET("servlet"),
+    PORTLET("portlet");
+
+    ContainerType(String displayName)
+    {
+      _displayName = displayName;
+    }
+    
+    public String displayName()
+    {
+      return _displayName;
+    }
+    
+    public static ContainerType valueOfDisplayName(String displayName)
+    {
+      return Enums.stringToEnum(_displayNameMap, displayName, ContainerType.class);
+    }
+    
+    private final String _displayName;
+    private static final Map<String, ContainerType> _displayNameMap;
+    
+    static
+    {
+      _displayNameMap = Enums.createDisplayNameMap(ContainerType.class);
+    }    
+  }
+  
+  // Enum that is used to indicate whether pregeneration should target  
+  // secure or nonsecure request types (or both).
+  public enum RequestType
+  {
+    NONSECURE("nonsecure"),
+    SECURE("secure");
+
+    RequestType(String displayName)
+    {
+      _displayName = displayName;
+    }
+    
+    public String displayName()
+    {
+      return _displayName;
+    }
+    
+    public static RequestType valueOfDisplayName(String displayName)
+    {
+      return Enums.stringToEnum(_displayNameMap, displayName, RequestType.class);
+    }
+    
+    private final String _displayName;
+    private static final Map<String, RequestType> _displayNameMap;
+    
+    static
+    {
+      _displayNameMap = Enums.createDisplayNameMap(RequestType.class);
+    } 
+  }
+
+  // Enum that is used to indicate whether pregeneration should target
+  // compressed or uncompressed style classes (or both).
+  public enum StyleClassType
+  {
+    COMPRESSED("compressed"),
+    UNCOMPRESSED("uncompressed");
+
+    StyleClassType(String displayName)
+    {
+      _displayName = displayName;
+    }
+    
+    public String displayName()
+    {
+      return _displayName;
+    }
+    
+    public static StyleClassType valueOfDisplayName(String displayName)
+    {
+      return Enums.stringToEnum(_displayNameMap, displayName, StyleClassType.class);
+    }
+    
+    private final String _displayName;
+    private static final Map<String, StyleClassType> _displayNameMap;
+    
+    static
+    {
+      _displayNameMap = Enums.createDisplayNameMap(StyleClassType.class);
+    } 
+  }
+
+  protected PregenConfig()
+  {
+  }
+
+  // A PregenConfig implementation that derives its contextual configuration 
+  // from request parameters.
+  private static abstract class ParamPregenConfig extends PregenConfig
+  {
+    public ParamPregenConfig(FacesContext context)
+      throws InvalidConfigException
+    {
+      ExternalContext external = context.getExternalContext();
+
+      _containerTypes = _parseDisplayNameParam(external,
+                                               "containerType",
+                                               ContainerType.class,
+                                               ContainerType.SERVLET);
+
+      _requestTypes = _parseDisplayNameParam(external,
+                                             "requestType",
+                                             RequestType.class,
+                                             RequestType.NONSECURE);
+      
+      _styleClassTypes = _parseDisplayNameParam(external,
+                                                "styleClassType",
+                                                StyleClassType.class,
+                                                StyleClassType.COMPRESSED);
+      
+      _targetDirectoryPath = _getTargetDirectoryPath(context);
+    }
+    
+    @Override
+    public Collection<ContainerType> getContainerTypes()
+    {
+      return _containerTypes;
+    }
+    
+    @Override
+    public Collection<RequestType> getRequestTypes()
+    {
+      return _requestTypes;
+    }
+    
+    @Override
+    public Collection<StyleClassType> getStyleClassTypes()
+    {
+      return _styleClassTypes;
+    }
+
+    @Override
+    public String getTargetDirectoryPath()
+    {
+      return _targetDirectoryPath;
+    }
+
+    // Returns the target directory path.  Throws an InvalidConfigException
+    // if the target directory does not exist/cannot be created or is not
+    // writable.
+    private static String _getTargetDirectoryPath(FacesContext context)
+      throws InvalidConfigException
+    {
+      String targetDirectoryPath = _getNormalizedTargetDirectoryPath(context);
+      
+      try
+      {
+        FileUtils.toWritableDirectory(targetDirectoryPath);
+      }
+      catch (IOException e)
+      {
+        // The directory either does not exist/cannot be created or
+        // exists but is not writable.  Propagate this failure out.
+        throw new InvalidConfigException(e.getMessage());
+      }
+      
+      return targetDirectoryPath;
+    }
+
+    // Returns the target directory path in its normalized form.
+    private static String _getNormalizedTargetDirectoryPath(FacesContext context)
+    {
+      String targetDirectoryPath = System.getProperty(_TARGET_DIRECTORY_PROPERTY);
+    
+      if ((targetDirectoryPath == null) || targetDirectoryPath.isEmpty())
+      {
+        // Refactor to avoid dependency on core renderkit-specific API?
+        targetDirectoryPath = CoreRenderingContext.getTemporaryDirectory(context, false);
+      }
+    
+      return _normalizeDirectoryPath(targetDirectoryPath);
+    }
+    
+    // Normalizes the specified directory path by stripping off the trailing
+    // file separator, if present.
+    private static String _normalizeDirectoryPath(String path)
+      throws InvalidConfigException
+    {
+      if ((path == null) || path.isEmpty())
+      {
+        String message = _LOG.getMessage("SKIN_PREGEN_NO_TARGET_DIRECTORY", 
+                                         _TARGET_DIRECTORY_PROPERTY);
+        throw new InvalidConfigException(message);
+      }
+
+      return _hasTrailingSeparator(path) ? 
+        path.substring(0, path.length() - 1) :
+        path;
+    }
+    
+    private static boolean _hasTrailingSeparator(String path)
+    {
+      char lastChar = path.charAt(path.length() - 1);
+      
+      // Explicitly check '/' just in case someone happens to be using
+      // this as a separator in their sys property value (and we're on
+      // a platform that uses a different separator, eg. Windows).
+      return ((lastChar == File.separatorChar) || (lastChar == '/'));
+    }
+
+    private final Collection<ContainerType>  _containerTypes;
+    private final Collection<RequestType>    _requestTypes;
+    private final Collection<StyleClassType> _styleClassTypes;
+    private final String                     _targetDirectoryPath;
+  }
+  
+  // PregenConfig implementation that is used for pregeneration of
+  // all possible variant values.
+  private static class AllPregenConfig extends ParamPregenConfig
+  {
+    public AllPregenConfig(FacesContext context)
+    {
+      super(context);
+    }
+
+    @Override
+    public Collection<Integer> getPlatformVariants()
+    {
+      return (Collection<Integer>)ALL_VARIANTS;
+    }
+    
+    @Override
+    public Collection<LocaleContext> getLocaleVariants()
+    {
+     return (Collection<LocaleContext>)ALL_VARIANTS;
+    }
+
+    @Override
+    public Collection<Integer> getReadingDirectionVariants()
+    {
+      return (Collection<Integer>)ALL_VARIANTS;
+    }
+
+    @Override
+    public Collection<Application> getAgentApplicationVariants()
+    {
+      return (Collection<Application>)ALL_VARIANTS;
+    }
+
+    @Override
+    public Collection<AccessibilityProfile> getAccessibilityVariants()
+    {
+      return (Collection<AccessibilityProfile>)ALL_VARIANTS;      
+    }
+  }
+
+  // PregenConfig implementation that is used for pregeneration of
+  // only the most common variant values.
+  private static class CommonPregenConfig extends ParamPregenConfig
+  {
+    public CommonPregenConfig(FacesContext context)
+    {
+      super(context);
+    }
+
+    @Override
+    public Collection<Integer> getPlatformVariants()
+    {
+      return Arrays.asList(
+               TrinidadAgent.OS_ANDROID,
+               TrinidadAgent.OS_IPHONE,
+               TrinidadAgent.OS_LINUX,
+               TrinidadAgent.OS_MACOS,
+               TrinidadAgent.OS_WINDOWS);
+    }
+    
+    @Override
+    public Collection<LocaleContext> getLocaleVariants()
+    {
+     return Arrays.asList(
+              NullLocaleContext.getLeftToRightContext(),
+              NullLocaleContext.getRightToLeftContext());
+    }
+
+    @Override
+    public Collection<Integer> getReadingDirectionVariants()
+    {
+      return Arrays.asList(LocaleUtils.DIRECTION_LEFTTORIGHT);
+    }
+
+    @Override
+    public Collection<Application> getAgentApplicationVariants()
+    {
+      return Arrays.asList(Application.GECKO,
+                           Application.IEXPLORER,
+                           Application.SAFARI);
+    }
+
+    @Override
+    public Collection<AccessibilityProfile> getAccessibilityVariants()
+    {
+      return Arrays.asList(AccessibilityProfile.getDefaultInstance());      
+    }
+  }
+  
+  // Null PregenConfig implementation. Only used for error cases, to
+  // avoid null comparisons.
+  private static final class NullPregenConfig extends PregenConfig
+  {
+    @Override
+    public Collection<Integer> getPlatformVariants()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<LocaleContext> getLocaleVariants()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<Integer> getReadingDirectionVariants()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<Application> getAgentApplicationVariants()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<AccessibilityProfile> getAccessibilityVariants()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<PregenConfig.ContainerType> getContainerTypes()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<PregenConfig.RequestType> getRequestTypes()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<PregenConfig.StyleClassType> getStyleClassTypes()
+    {
+      return Collections.emptySet();
+    }
+    
+    @Override
+    public String getTargetDirectoryPath()
+    {
+      return null;
+    }
+  }
+
+  private static final PregenConfig _NULL_CONFIG = new NullPregenConfig();
+
+  private static final String _TARGET_DIRECTORY_PROPERTY =
+    "org.apache.myfaces.trinidad.SKIN_PREGENERATION_SERVICE_TARGET_DIRECTORY";
+
+  private static final TrinidadLogger _LOG =
+    TrinidadLogger.createTrinidadLogger(PregenConfig.class);  
+}

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/context/PregenStyleContext.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/context/PregenStyleContext.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/context/PregenStyleContext.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/context/PregenStyleContext.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.skin.pregen.context;
+
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.trinidad.context.AccessibilityProfile;
+import org.apache.myfaces.trinidad.context.Agent;
+import org.apache.myfaces.trinidad.context.LocaleContext;
+import org.apache.myfaces.trinidad.style.Styles;
+import org.apache.myfaces.trinidadinternal.agent.AgentNameUtil;
+import org.apache.myfaces.trinidadinternal.agent.DefaultAgent;
+import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent;
+import org.apache.myfaces.trinidadinternal.agent.TrinidadAgentImpl;
+import org.apache.myfaces.trinidadinternal.skin.pregen.config.PregenConfig.ContainerType;
+import org.apache.myfaces.trinidadinternal.skin.pregen.config.PregenConfig.RequestType;
+import org.apache.myfaces.trinidadinternal.skin.pregen.config.PregenConfig.StyleClassType;
+import org.apache.myfaces.trinidadinternal.skin.pregen.variant.ApplicationAndVersion;
+import org.apache.myfaces.trinidadinternal.skin.pregen.variant.SkinVariant;
+import org.apache.myfaces.trinidadinternal.style.StyleContext;
+import org.apache.myfaces.trinidadinternal.style.StyleProvider;
+import org.apache.myfaces.trinidadinternal.style.StyleSheetNamingStrategy;
+
+/**
+ * StyleContext implementation used during skin pregeneration.
+ */
+public final class PregenStyleContext implements StyleContext
+{
+  /**
+   * Returns a StyleContext instance that can be used to retrieve the
+   * StyleSheetDocument for the specified StyleProvider.
+   * 
+   * @param provider the StyleProvider for which we'll retrieve the StyleSheetDocument
+   * @param path the generated files path
+   * @return
+   */
+  public static StyleContext documentContext(
+    FacesContext  context,
+    StyleProvider provider,
+    String        path
+    )
+  {
+    // Note: DocumentProviderSkin.getStyleSheetDocument() minimally requires
+    // the StyleContet's StyleProvider and the generated files path, so that is
+    // all that we bother with here.
+    return new PregenStyleContext(context,
+                                  provider, 
+                                  path, 
+                                  null,
+                                  false);
+  }
+
+  /**
+   * Creates a StyleContext that can be used for pregeneration
+   * of a single skin variant.
+   * 
+   * @param context the FacesContext
+   * @param provider the StyleProvider
+   * @param generatedFilesPath the root generated files path
+   * @param variant the variant of the skin to generate
+   */
+  public PregenStyleContext(
+    FacesContext           context,
+    StyleProvider          provider,
+    String                 generatedFilesPath,
+    SkinVariant            variant,
+    boolean                dirty
+    )
+  {
+    _styleProvider = provider;
+    _generatedFilesPath = generatedFilesPath;
+    _variant = variant;
+    _agent = _createTrinidadAgent(context, variant);
+    _dirty = dirty;
+  }
+  
+  @Override
+  public LocaleContext getLocaleContext()
+  {
+    return _variant.getLocaleContext();
+  }
+
+  @Override
+  public TrinidadAgent getAgent()
+  {
+    return _agent;
+  }
+
+  @Override
+  public String getGeneratedFilesPath()
+  {
+    return _generatedFilesPath;
+  }
+  
+  @Override
+  public StyleSheetNamingStrategy getNamingStrategy()
+  {
+    // Stable names are required for pregeneration in order
+    // for runtime names to match pregenerated names.
+    return StyleSheetNamingStrategy.STABLE;
+  }
+
+  @Override
+  public boolean checkStylesModified()
+  {
+    return false;
+  }
+
+  @Override
+  public boolean disableStandardsMode()
+  {
+    return false;
+  }
+
+  @Override
+  public StyleProvider getStyleProvider()
+  {
+    return getStyleProvider(false);
+  }
+
+  @Override
+  public StyleProvider getStyleProvider(boolean recompute)
+  {
+    return _styleProvider;
+  }
+
+  @Override
+  public Styles getStyles()
+  {
+    return null;
+  }
+
+  @Override
+  public AccessibilityProfile getAccessibilityProfile()
+  {
+    return _variant.getAccessibilityProfile();
+  }
+
+  @Override
+  public boolean isPortletMode()
+  {
+    return (_variant.getContainerType() == ContainerType.PORTLET);
+  }
+
+  @Override
+  public boolean isDisableStyleCompression()
+  {
+    return (_variant.getStyleClassType() == StyleClassType.UNCOMPRESSED);
+  }
+
+  @Override
+  public boolean isDirty()
+  {
+    return _dirty;
+  }
+
+  @Override
+  public boolean isRequestSecure()
+  {
+    return (_variant.getRequestType() == RequestType.SECURE);
+  }
+  
+  private static TrinidadAgent _createTrinidadAgent(
+    FacesContext          context,
+    SkinVariant           variant
+    )
+  {
+    if (variant == null)
+    {
+      // Null variant == bootstrapping case.  No need for agent access.
+      return null;
+    }
+                            
+    // In theory we should be able to create/use our own trivial TrinidadAgent
+    // implementation.  However, TrinidadAgentImpl contains all of the magic
+    // for figuring out agent capabilities.  Turns out to be easier if we
+    // just use this implementation rather than create our own.  Unfortunately,
+    // this means that we first need to jump through the hoop of translating
+    // the agent information into org.apache.myfaces.trinidad.context.Agent
+    // land.
+    Agent agent = new PregenAgent(variant.getPlatform(), variant.getApplicationAndVersion());
+
+    return new TrinidadAgentImpl(context, agent);
+  }
+  
+  private static class PregenAgent extends DefaultAgent
+  {
+    public PregenAgent(
+      int                   platform,
+      ApplicationAndVersion appAndVersion
+      )
+    {
+      assert(appAndVersion != null);
+      
+      _platformName = AgentNameUtil.getPlatformName(platform);
+      _agentName = appAndVersion.application.getAgentName();
+      _agentVersion = appAndVersion.version.toString();
+    }
+    
+    @Override
+    public String getAgentName()
+    {
+      return _agentName;  
+    }
+    
+    @Override
+    public String getAgentVersion()
+    {
+      return _agentVersion;
+    }
+    
+    @Override
+    public String getPlatformName()
+    {
+      return _platformName;
+    }
+    
+    private final String _agentName;
+    private final String _agentVersion;
+    private final String _platformName;
+  }
+
+  private final StyleProvider _styleProvider;
+  private final String        _generatedFilesPath;
+  private final SkinVariant   _variant;
+  private final TrinidadAgent _agent;
+  private final boolean       _dirty;
+}

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/context/PregenStyleProvider.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/context/PregenStyleProvider.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/context/PregenStyleProvider.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/context/PregenStyleProvider.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.skin.pregen.context;
+
+import org.apache.myfaces.trinidad.skin.Skin;
+import org.apache.myfaces.trinidadinternal.skin.SkinStyleProvider;
+
+/**
+ * StyleProvider implementation used during skin pregeneration.
+ * 
+ * During skin pregeneration we periodically clear out the
+ * StyleProvider in order to free up memory.  However, when
+ * we do this, we don't want to force a re-parse of the skin
+ * definitions.  We override getClearOnDirty() to avoid this.
+ */
+public class PregenStyleProvider extends SkinStyleProvider
+{
+  public PregenStyleProvider(Skin skin, String targetDirectoryPath)
+    throws IllegalArgumentException
+  {
+    super(skin, targetDirectoryPath);
+  }
+  
+  @Override
+  protected ClearOnDirty getClearOnDirty()
+  {
+    // Only clear out cache entries - don't blow away the
+    // StyleSheetDocument and associated data.
+    return ClearOnDirty.ENTRIES;
+  }
+}

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/AccessibilityVariantExtractor.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/AccessibilityVariantExtractor.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/AccessibilityVariantExtractor.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/AccessibilityVariantExtractor.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.skin.pregen.variant;
+
+import java.util.ArrayList;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+import org.apache.myfaces.trinidad.context.AccessibilityProfile;
+import org.apache.myfaces.trinidadinternal.style.xml.XMLConstants;
+import org.apache.myfaces.trinidadinternal.style.xml.parse.StyleSheetNode;
+
+/**
+ * Helper class for extracting @accessibility-profile rule metadata.
+ */
+final class AccessibilityVariantExtractor implements SkinVariantExtractor<AccessibilityProfile>
+{
+  public AccessibilityVariantExtractor()
+  {
+    _accProfiles = new HashSet<AccessibilityProfile>();
+    
+    // Seed with default accessibility profile. This won't show
+    // up as at @accessibility rule, but needs to be covered during
+    // pregeneration.
+    _accProfiles.add(AccessibilityProfile.getDefaultInstance());
+  }
+
+  @Override
+  public void visit(StyleSheetNode node)
+  {
+    Collection<String> accProps = node.getAccessibilityProperties();
+    
+    for (String accProp : accProps)
+    {
+      _addAccessibilityProfile(accProp);
+    }
+  }
+
+  /**
+   * Returns un unmodifiable list containing AccessibilityProfiles corresponding
+   * to all @accesibility-profile rules in the specified document.
+   */
+  public List<AccessibilityProfile> getVariants()
+  {
+    _addCompoundAccessibilityProfile();
+ 
+    List<AccessibilityProfile> accProfilesList =
+      new ArrayList<AccessibilityProfile>(_accProfiles);
+  
+    return accProfilesList;
+  }
+  
+  // Adds the compound high contrast + large fonts profile
+  // if necessary.
+  private void _addCompoundAccessibilityProfile()
+  {
+    // In cases where the skin contains both:
+    //
+    // - @accessibility-profile high-contrast, and...
+    // - @accessibility-profile large-fonts
+    //
+    // Then we need to generate a style sheet for end users
+    // that happen to run with both high contrast and large fonts
+    // enabled.  Thus, we add in a compound high contrast + large
+    // fonts profile if we've seen each property individually.
+    if (_accProfiles.contains(_HIGH_CONTRAST_ONLY) &&
+        _accProfiles.contains(_LARGE_FONTS_ONLY)   &&
+        !_accProfiles.contains(_HIGH_CONTRAST_LARGE_FONTS))
+    {
+      _accProfiles.add(_HIGH_CONTRAST_LARGE_FONTS);
+    }
+  }
+
+  private void _addAccessibilityProfile(String accProp)
+  {
+    AccessibilityProfile accProfile = _toAccessibilityProfile(accProp);
+
+    _accProfiles.add(accProfile);                                                               
+  }
+  
+  private static AccessibilityProfile _toAccessibilityProfile(String accProp)
+  {
+    boolean highContrast = false;
+    boolean largeFonts = false;
+    
+    if (_isHighContrast(accProp))
+    {
+      highContrast = true;
+    }
+    else if (_isLargeFonts(accProp))
+    {
+      largeFonts = true;
+    }
+    else if (_isHighContrastLargeFonts(accProp))
+    {
+      highContrast = true;
+      largeFonts = true;
+    }
+    
+    return _getAccessibilityProfile(highContrast, largeFonts);
+  }
+
+  private static boolean _isHighContrast(String accProp)
+  {
+    return XMLConstants.ACC_HIGH_CONTRAST.equals(accProp);
+  }
+  
+  private static boolean _isLargeFonts(String accProp)
+  {
+    return XMLConstants.ACC_LARGE_FONTS.equals(accProp);
+  }
+
+  private static boolean _isHighContrastLargeFonts(String accProp)
+  {
+    return ((accProp.indexOf(XMLConstants.ACC_HIGH_CONTRAST) > -1) &&
+            (accProp.indexOf(XMLConstants.ACC_LARGE_FONTS) > -1));
+  }
+
+  private static AccessibilityProfile _getAccessibilityProfile(
+    boolean highContrast,
+    boolean largeFonts
+    )
+  {
+    return AccessibilityProfile.getInstance(
+             highContrast ?
+               AccessibilityProfile.ColorContrast.HIGH :
+               AccessibilityProfile.ColorContrast.STANDARD,
+             largeFonts ?
+               AccessibilityProfile.FontSize.LARGE :
+               AccessibilityProfile.FontSize.MEDIUM);
+  }
+
+  private final Collection<AccessibilityProfile> _accProfiles;
+
+  private static final AccessibilityProfile _HIGH_CONTRAST_ONLY =
+    _getAccessibilityProfile(true, false);  
+  private static final AccessibilityProfile _LARGE_FONTS_ONLY =
+    _getAccessibilityProfile(false, true);
+  private static final AccessibilityProfile _HIGH_CONTRAST_LARGE_FONTS =
+    _getAccessibilityProfile(true, true);
+}

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/AgentVariantExtractor.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/AgentVariantExtractor.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/AgentVariantExtractor.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/AgentVariantExtractor.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.skin.pregen.variant;
+
+import java.util.AbstractCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import java.util.Collections;
+import java.util.HashMap;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.myfaces.trinidad.context.Version;
+import org.apache.myfaces.trinidad.util.Range;
+
+import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent.Application;
+import org.apache.myfaces.trinidadinternal.skin.AgentAtRuleMatcher;
+import org.apache.myfaces.trinidadinternal.style.xml.parse.StyleSheetNode;
+
+/**
+ * An @-rule processor for extracting @agent rule metadata.
+ */
+final class AgentVariantExtractor implements SkinVariantExtractor<ApplicationAndVersion>
+{
+  /**
+   * Creates an AgentVariantExtractor for a specified set of supported
+   * agent applications.  AgentVariantExtractor.getVariants() will only
+   * ApplicationAndVersion instances corresponding to these agent
+   * applications.  If no supported agent applications are specified,
+   * all agent applications found in the style sheet nodes will be 
+   * treated as supported.
+   */
+  public AgentVariantExtractor(
+    Collection<Application> supportedApplications
+    )
+  {
+    _appVersionsMap =
+      new HashMap<Application, Set<Version>>();
+    
+    _supportedApplications = _initSupportedApplications(supportedApplications);
+    
+    // Seed the map with unknown agent.  This won't appear
+    // in the skin definition, but we need to cover this case
+    // during pregeneration.
+    _addApplicationIfSupported(Application.UNKNOWN);  
+  }
+  
+  private Collection<Application> _initSupportedApplications(
+    Collection<Application> supportedApplications
+    )
+  {
+    if ((supportedApplications == null) || supportedApplications.isEmpty())
+    {
+      return new AbstractCollection<Application>() {
+        
+          @Override
+          public boolean contains(Object o)
+          {
+            return true;  
+          }
+          
+          @Override
+          public Iterator<Application> iterator()
+          {
+            throw new UnsupportedOperationException();
+          }
+
+          @Override
+          public int size()
+          {
+            throw new UnsupportedOperationException();
+          }
+        };
+    }
+
+    return new HashSet<Application>(supportedApplications);
+  }
+
+  @Override
+  public void visit(StyleSheetNode node)
+  {
+    AgentAtRuleMatcher agentMatcher = node.getAgentMatcher();
+    
+    if (agentMatcher != null)
+    {
+      _addApplicationVersions(agentMatcher);    
+    }
+  }
+
+  /**
+   * Returns alist containing ApplicationAndVersions
+   * corresponding to all processed @agent rules.
+   */
+  public List<ApplicationAndVersion> getVariants()
+  {
+    List<ApplicationAndVersion> appAndVersionsList =
+      _toAppAndVersionsList(_appVersionsMap);
+
+    return appAndVersionsList;
+  }
+  
+  private void _addApplicationVersions(AgentAtRuleMatcher agentMatcher)
+  {
+    assert(agentMatcher != null);
+    
+    Collection<Application> nodeApplications =
+      agentMatcher.getAllApplications();
+      
+    for (Application application : nodeApplications)
+    {
+
+      boolean supported = _addApplicationIfSupported(application);
+      
+      if (supported)
+      {
+        Collection<Range<Version>> versionRanges = agentMatcher.getAllVersionsForApplication(application);
+        _addVersions(application, versionRanges);        
+      }
+
+    }    
+  }
+  
+  private boolean _addApplicationIfSupported(Application application)
+  {
+    if (!_supportedApplications.contains(application))
+    {
+      return false;
+    }
+
+    if (!_appVersionsMap.containsKey(application))
+    {
+      Set<Version> versions = new TreeSet<Version>();
+      
+      // Minimally, every application needs to be able
+      // to pregenerate for the unknown version case.
+      versions.add(_UNKNOWN_VERSION);
+      
+      _appVersionsMap.put(application, versions);
+    }
+    
+    return true;
+  }
+  
+  private void _addVersions(
+    Application  application,
+    Collection<Range<Version>> versionRanges
+    )
+  {
+    Collection<Version> versions = _appVersionsMap.get(application);
+    assert(versions != null);
+    
+    for (Range<Version> versionRange : versionRanges)
+    {
+      // We add the start/end points of the range to our
+      // set of versions to pregenerate.
+      
+      // Also note that from here on out we only want to
+      // deal with "concrete" versions.  If we leave version
+      // wildcards in place, we can lose information due to
+      // Version's wildcard-sensitive natural ordering (ie.
+      // a Set.add() might fail because a matching/wildcarded
+      // version is already present.)
+      versions.add(versionRange.getStart().toMinimumVersion());
+      versions.add(versionRange.getEnd().toMaximumVersion());
+    }
+  }
+
+  private static List<ApplicationAndVersion> _toAppAndVersionsList(
+    Map<Application, Set<Version>> appVersionsMap
+    )
+  {
+    List<ApplicationAndVersion> appAndVersions = 
+      new ArrayList<ApplicationAndVersion>();
+    
+    for (Map.Entry<Application, Set<Version>> entry : appVersionsMap.entrySet())
+    {
+      for (Version version : entry.getValue())
+      {
+        appAndVersions.add(new ApplicationAndVersion(entry.getKey(), version));
+      }
+    }
+
+    // Sort to make logger output/progress easier to monitor
+    Collections.sort(appAndVersions);
+
+    return appAndVersions;
+  }
+
+  // Map of application to versions that have been encountered 
+  // during processing
+  private final Map<Application, Set<Version>> _appVersionsMap;
+  
+  // Only extract version information for these applications.
+  private final Collection<Application> _supportedApplications;
+
+  // A Version that we use to ensure that we pregenerate style sheets
+  // for the case where the agent version does not match any version
+  // specified in the skin.
+  private static final Version _UNKNOWN_VERSION = new Version("unknown");
+}

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/ApplicationAndVersion.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/ApplicationAndVersion.java?rev=1303525&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/ApplicationAndVersion.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/pregen/variant/ApplicationAndVersion.java Wed Mar 21 19:33:45 2012
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.skin.pregen.variant;
+
+import org.apache.myfaces.trinidad.context.Version;
+import org.apache.myfaces.trinidad.util.Args;
+import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent.Application;
+
+/**
+ * A convenience duple class that holds onto a TrinidadAgent.Application and
+ * Version value.
+ * 
+ * Only note of interest: the AppicationAndVersion.version is guaranteed to
+ * be concrete (ie. no wildcards).  As a result, although Version.equals()
+ * and Version.compareTo() are typically inconsistent, ApplicationAndVersion's
+ * equals() and compareTo() implementations are consistent - ie. if
+ * ApplicationAndVersion.compareTo() returns zero, equals() returns true
+ * (and vice versa).
+ */
+public final class ApplicationAndVersion implements Comparable<ApplicationAndVersion>
+{
+  public final Application application;
+  public final Version version;
+
+  /**
+   * An instance that can be used as a placeholder for cases
+   * where the application and version are not known.
+   */
+  public static final ApplicationAndVersion UNKNOWN = 
+    new ApplicationAndVersion(
+      Application.UNKNOWN,
+      Version.MIN_VERSION);
+  
+  /**
+   * Creates an AppplicationAndVersion instance for the
+   * specified application and version.
+   * 
+   * If the Version contains wildcards, this will be replaced by a new,
+   * matching, concrete (wildcard-free) Version instance.
+   * 
+   * @param application a non-null TrinidadAgent.Application instance.
+   * @param version a non-null Version instance.
+   * 
+   * @throws IllegalArgumentException if either application or version are null.
+   */
+  public ApplicationAndVersion(
+    Application application,
+    Version version
+    ) throws IllegalArgumentException
+  {
+    Args.notNull(application, "application");
+    Args.notNull(version, "version");
+
+    this.application = application;
+    
+    // Any concrete version will do, we'll go with the min.
+    this.version = version.toMinimumVersion();
+  }
+  
+  @Override
+  public String toString()
+  {
+    return this.application + "v" + this.version;
+  }
+
+  @Override
+  public int compareTo(ApplicationAndVersion otherAppAndVersion)
+  {
+    String agentName = this.application.getAgentName();
+    String otherAgentName = otherAppAndVersion.application.getAgentName();
+
+    int nameResult = agentName.compareTo(otherAgentName);
+    if (nameResult != 0)
+    {
+      return nameResult;
+    }
+    
+    return this.version.compareTo(otherAppAndVersion.version);
+  }
+  
+  @Override
+  public boolean equals(Object o)
+  {
+    if (this == o)
+    {
+      return true;
+    }
+    
+    if (!(o instanceof ApplicationAndVersion))
+    {
+      return false;
+    }
+    
+    ApplicationAndVersion appAndVersion = (ApplicationAndVersion)o;
+    
+    return (this.application.equals(appAndVersion.application) &&
+            this.version.equals(appAndVersion.version));
+  }
+  
+  @Override
+  public int hashCode()
+  {
+    int result = 17;
+    
+    result = 31 * result + this.application.hashCode();
+    result = 31 * result + this.version.hashCode();
+    
+    return result;
+  }
+}