Package org.apache.myfaces.trinidadinternal.style.cache

Source Code of org.apache.myfaces.trinidadinternal.style.cache.FileSystemStyleCache$Key

/*
*  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.style.cache;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.myfaces.trinidad.context.AccessibilityProfile;
import org.apache.myfaces.trinidad.context.LocaleContext;
import org.apache.myfaces.trinidad.context.RenderingContext;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.skin.Icon;
import org.apache.myfaces.trinidad.skin.Skin;
import org.apache.myfaces.trinidad.style.Selector;
import org.apache.myfaces.trinidad.style.Style;
import org.apache.myfaces.trinidad.style.Styles;
import org.apache.myfaces.trinidad.util.CollectionUtils;
import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent;
import org.apache.myfaces.trinidadinternal.renderkit.core.CoreRenderingContext;
import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.SkinSelectors;
import org.apache.myfaces.trinidadinternal.style.CSSStyle;
import org.apache.myfaces.trinidadinternal.style.StyleContext;
import org.apache.myfaces.trinidadinternal.style.StyleProvider;
import org.apache.myfaces.trinidadinternal.style.util.CSSGenerationUtils;
import org.apache.myfaces.trinidadinternal.style.util.NameUtils;
import org.apache.myfaces.trinidadinternal.style.util.StyleWriterFactory;
import org.apache.myfaces.trinidadinternal.style.xml.parse.IconNode;
import org.apache.myfaces.trinidadinternal.style.xml.parse.PropertyNode;
import org.apache.myfaces.trinidadinternal.style.xml.parse.SkinPropertyNode;
import org.apache.myfaces.trinidadinternal.style.xml.parse.StyleNode;
import org.apache.myfaces.trinidadinternal.style.xml.parse.StyleSheetDocument;
import org.apache.myfaces.trinidadinternal.style.xml.parse.StyleSheetNode;
import org.apache.myfaces.trinidadinternal.util.nls.LocaleUtils;


/**
* The FileSystemStyleCache is a StyleProvider implementation which
* caches generated CSS style sheets on the file system.
*
* Note that StyleProviders are responsible for providing access
* both to style information (eg. getStyleSheetURI(), getStyles()) as
* well as to icons registered via style sheets (see getIcons()).
*
* @see org.apache.myfaces.trinidadinternal.style.StyleProvider
* @see org.apache.myfaces.trinidadinternal.skin.SkinStyleProvider
*
* @version $Name:  $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/style/cache/FileSystemStyleCache.java#0 $) $Date: 10-nov-2005.18:58:54 $
*/
public class FileSystemStyleCache implements StyleProvider
{
  /**
   * Returns the mime type for the styles provided by this
   * FileSystemStyleCache - "text/css".
   */
  public String getContentStyleType(StyleContext context)
  {
    return "text/css";
  }

  /**
   * Creates a FileSystemStyleCache.
   *
   * @param target The path of the target directory.  Generated
   *   CSS files are stored in this directory.  If the directory
   *   does not exist and can not be created, an IllegalArgumentException
   *   is thrown.
   *  @see org.apache.myfaces.trinidadinternal.skin.SkinStyleProvider - the subclass
   */
  protected FileSystemStyleCache(String target)
  {
    // If the target directory does not exist, create it now.
    // Note: If we can't create the target directory, we just
    // plug along anyway instead of throwing an IllegalArgumentException.
    // That way, we can still use the Styles for this style sheet
    // even if the style sheet isn't generated.
    File targetDirectory = new File(target);
    if (!targetDirectory.exists())
      targetDirectory.mkdirs();

    _targetPath = target;
  }

  /**
   * Implementation of StyleCache.getStyleSheetURI().
   */
  public List<String> getStyleSheetURIs(StyleContext context)
  {
    Entry entry = _getEntry(context);

    if (entry == null)
    {
      return Collections.emptyList();
    }
    else
    {
      return entry.uris;
    }
  }

  /**
   * Implementation of StyleProvider.getStyles().
   */
  public Styles getStyles(StyleContext context)
  {

    Entry entry = _getEntry(context);

    if (entry == null)
      return null;
    else
      return entry.styles;
  }

  /**
   * Implementation of StyleProvider.getSkinProperties()
   */
  public ConcurrentMap<Object, Object> getSkinProperties(StyleContext context)
  {

    Entry entry = _getEntry(context);

    if (entry == null)
      return null;
    else
      return entry.skinProperties;
  }

  /**
   * Implementation of StyleProvider.getIcons()
   */
  public ConcurrentMap<String, Icon> getIcons(StyleContext context)
  {

    Entry entry = _getEntry(context);

    if (entry == null)
      return null;
    else
      return entry.icons;
  }

  /**
   * Returns a Map which maps style class names to
   * equivalent shorter names.
   * <p>
   * FileSystemStyleCache automatically generates short versions
   * of every style class that is found the the underlying XSS
   * document.  FileSystemStyleCache clients can reduce the
   * size of generated content by using this method to obtain
   * short versions of any rendered style classes.
   * <p>
   * Note: The returned Map uses String keys to represent
   * the full class names.  However, the short style class values
   * may not necessarily be type java.lang.String.  Clients must
   * avoid explicitly casting the values contained in the Map
   * to type String.  Instead, such values should be passed directly
   * to the ResponseWriter API to be rendered.  Or, if the String
   * representation is required, toString() should be called on
   * the value.
   *
   * @param context The StyleContext
   *
   * @return A Map which maps the full style class names to
   *   the shorter equivalents.
   */
  public Map<String, String> getShortStyleClasses(StyleContext context)
  {
    return _shortStyleClassMap;
  }

  /**
   * Creates the StyleSheetDocument for this StyleProvider.
   * @param context The StyleContext
   *                (not needed here, but is needed in  subclass)
   * @return The StyleSheetDocument which defines the styles
   *         for this StyleProvider.
   * @see org.apache.myfaces.trinidadinternal.skin.SkinStyleProvider#createStyleSheetDocument(context)
   */
  protected StyleSheetDocument createStyleSheetDocument(
    StyleContext context
    )
  {
    return null;
  }

  /**
   * Tests whether the source style sheet files have been modified
   * since the last call to createStyleSheetDocument().
   * @return true if the underlying source style sheets have been
   *              modified, false otherwise.
   */
  protected boolean hasSourceDocumentChanged(StyleContext context)
  {
    // If we haven't parsed yet, don't bother checking the time stamp
    if (_document == null)
      return true;
    return false;
  }

  /**
   * Returns the name to use for the generated style sheet file .
   *
   * @param context The StyleContext
   * @param document The StyleSheetDocument which provides the styles
   */
  protected String getTargetStyleSheetName(
    StyleContext       context,
    StyleSheetDocument document
    )
  {
    StringBuilder buffer = new StringBuilder();

    String contextName = NameUtils.getContextName(context, document);
    if ((contextName != null) && contextName.length() > 0)
    {
      buffer.append(contextName);
    }


    boolean compressedStyles = _isCompressStyles(context);
    if (compressedStyles)
    {
      if (contextName != null)
        buffer.append(_NAME_SEPARATOR);
      buffer.append(_COMPRESSED);
    }

    if (context.isPortletMode())
    {
      if (contextName != null || compressedStyles)
        buffer.append(_NAME_SEPARATOR);

      buffer.append(_PORTLET);
    }

    buffer.append(_CSS_EXTENSION);

    return buffer.toString();
  }

  // Returns the current StyleSheetDocument - used by StyleMapImpl only
  StyleSheetDocument __getStyleSheetDocument()
  {
    return _document;
  }

  // Gets the entry for the specified StyleContext, creating it if
  // necessary. Part of creating an entry is creating the style sheet file itself.
  // And Entry contains the style sheet URI.
  private Entry _getEntry(StyleContext context)
  {
    ConcurrentMap<Key, Entry> cache = null;
    ConcurrentMap<Object, Entry> entryCache = null;
    StyleSheetDocument document = null;
    Map<String, String> shortStyleClassMap = null;
    String[] namespacePrefixes = null;
    RenderingContext arc = RenderingContext.getCurrentInstance();
    Skin skin = arc.getSkin();
    boolean isDirty = skin.isDirty();
    boolean checkModified  = context.checkStylesModified();

    // Synchronize while set up the _cache, _entryCache, _document, etc...
    synchronized (this)
    {
      // If checking for modified files, then check to see if the XSS or CSS
      // document has been modified.  If so, we dump our in-memory style cache.
      if (isDirty || (checkModified && hasSourceDocumentChanged(context)))
      {
        _cache = null;
        _entryCache = null;
        _document = null;
        _shortStyleClassMap = null;
        _namespacePrefixes  = null;
      }

      // We get references to our two caches (the "normal" cache,
      // and the cache of shared Entry objects) up front.  We do
      // this because the actual caches could change at any time.
      // (The caches get reallocated if the source document is
      // modified.)  We need to use a consistent set of caches
      // throughout the entire request, to avoid adding bogus entries
      // to a new re-allocated cache.
      if (_cache == null)
        _cache = new ConcurrentHashMap<Key, Entry>();
      if (_entryCache == null)
        _entryCache = new ConcurrentHashMap<Object, Entry>(19);

      cache = _cache;
      entryCache = _entryCache;

      // Get the document up front too.
      // Returns the StyleSheetDocument, parsing the source file if necessary
      // this sets up _shortStyleClassMap and _namespacePrefixes
      document = _getStyleSheetDocument(context);
      if (document == null)
        return null;

      shortStyleClassMap = _shortStyleClassMap;
      namespacePrefixes = _namespacePrefixes;
    }

    // Look up the style sheet
    // The Key class is a private static class that is used for hashing. It implements
    // hashCode and equals which are based on locale, direction, browser, version, platform.
    Key key = new Key(context);
    if (_LOG.isFinest())
    {
      _LOG.finest("FileSystemStyleCache's Key's hashCode is ", key.hashCode());
    }
    Entry entry = _getEntry(cache, key, checkModified);
    if (entry != null)
      return entry;

    // Next see if this is an entry which is compatible with this request
    entry = _getCompatibleEntry(context, document, cache, key, entryCache, checkModified);

    if (entry != null)
      return entry;

    // If we didn't find an entry in the cache, create a new entry
    // This generates the CSS file.
    return _createEntry(context,
                        document,
                        cache,
                        key,
                        entryCache,
                        shortStyleClassMap,
                        namespacePrefixes,
                        checkModified,
                        isDirty);
  }

  private Entry _getEntry(
    ConcurrentMap<?, Entry> cache,
    Object                  key,
    boolean                 checkModified
    )
  {
    Entry entry = cache.get(key);
    if (entry == null)
    {
      return null;
    }

    if (checkModified)
    {
      List<String> uris = entry.uris;
      assert uris != null && !uris.isEmpty();

      boolean valid = true;
      List<File> existing = new LinkedList<File>();
      // Make sure the entry's file exists.  If it no longer
      // exists, we remove the entry from the cache
      for (String name : uris)
      {
        File file = new File(_targetPath, name);
        if (file.exists())
        {
          existing.add(file);
        }
        else
        {
          valid = false;
        }
      }

      if (!valid)
      {
        _deleteAll(existing);
       
        // atomically remove the key from the cache if it currently points to the entry
        cache.remove(key, entry);

        return null;
      }
    }

    return entry;
  }

  /**
   * Creates and caches an Entry for the specified StyleContext
   * This generates a style sheet for the specific StyleContext
   * (locale, direction, etc), and puts that style sheet's uri in the Entry.
   * It also caches it in the "normal" cache (the one that is based on the StyleContext),
   * and the entry cache (the one that is based on the StyleSheetNodes)
   */
  private Entry _createEntry(
    StyleContext                 context,
    StyleSheetDocument           document,
    ConcurrentMap<Key, Entry>    cache,
    Key                          key,
    ConcurrentMap<Object, Entry> entryCache,
    Map<String, String>          shortStyleClassMap,
    String[]                     namespacePrefixes,
    boolean                      checkModified,
    boolean                      isDirty)
  {
    // Next, get the fully resolved styles for this context. This will be
    // those StyleNodes that match the locale, direction, browser, portlet mode
    // etc -- the info that is in the StyleContext.
    // These styles contain all the StyleNodes, that is, where selector or
    // name (aka alias) are non-null.
    StyleNode[] styleNodes = _getStyleContextResolvedStyles(context, document);
    if (styleNodes == null)
      return null;


    // Generate the style sheet file, if it isn't already generated,
    // and return the uri.
    // Only the StyleNodes with non-null selectors get written to the
    // generated css file.
    // Named styles (StyleNode where name != null) do not get
    // written to the generated css file.
    List<String> uris = _createStyleSheetFiles(context,
                                       document,
                                       styleNodes,
                                       shortStyleClassMap,
                                       namespacePrefixes,
                                       checkModified,
                                       isDirty);

    _LOG.fine("Finished processing stylesheet {0}", uris);


    // Next, get the fully resolved icons and skin properties for this context.
    // This will be those Icons and Skin Properties that match the locale, direction,
    // browser, etc -- the info that is in the StyleContext
    ConcurrentMap<String, Icon> icons =
      _getStyleContextResolvedIcons(context, document);
    ConcurrentMap<Object, Object> skinProperties =
      _getStyleContextResolvedSkinProperties(context, document);

    // Create a new entry and cache it in the "normal" cache. The "normal" cache is one
    // where the key is the Key object which is built based on information from the StyleContext,
    // like browser, agent, locale, direction.
    Styles styles = new StylesImpl(styleNodes, namespacePrefixes, _STYLE_KEY_MAP,
                                   shortStyleClassMap,  _isCompressStyles(context));
    Entry entry = new Entry(uris, styles, icons, skinProperties);
    cache.put(key, entry);

    // Also, cache the new entry in the entry cache
    DerivationKey derivationKey = _getDerivationKey(context, document);
    entryCache.put(derivationKey, entry);
   
    // just in case, clear the dirty flag.
    RenderingContext arc = RenderingContext.getCurrentInstance();
    Skin skin = arc.getSkin();
    skin.setDirty(false);

    return entry;
  }

  /**
   * Look in the entry cache for a compatible entry.
   * A compatible entry is one with the same DerivationKey, which is essentially the
   * same StyleSheetNodes.
   */
  private Entry _getCompatibleEntry(
    StyleContext                 context,
    StyleSheetDocument           document,
    ConcurrentMap<Key, Entry>    cache,
    Key                          key,
    ConcurrentMap<Object, Entry> entryCache,
    boolean                      checkModified
    )
  {
    DerivationKey derivationKey = _getDerivationKey(context, document);
    Entry entry = _getEntry(entryCache, derivationKey, checkModified);
    if (entry != null)
    {
      // If we've got a compatible entry, cache it
      cache.put(key, entry);
    }
    return entry;
  }

  /**
   * Returns the derivation key that would be used for entries
   * based on the provided context
   */
  private DerivationKey _getDerivationKey(
    StyleContext context,
    StyleSheetDocument document
    )
  {
    // Entries with the same style sheet derivation are compatible.
    // Get the style sheet derivation list.
    Iterator<StyleSheetNode> e = document.getStyleSheets(context);

    StyleSheetNode[] styleSheets;
   
    if (e.hasNext())
    {
      styleSheets = CollectionUtils.toArray(e, StyleSheetNode.class);
    }
    else
    {
      styleSheets = _EMPTY_STYLE_SHEET_NODE_ARRAY;
    }

    // Create a key out of the style sheet derivation list
    return new DerivationKey(context, styleSheets);
  }

  /**
   * Returns the StyleSheetDocument, parsing the source file if necessary.
   * This does not use the StyleContext
   */
  private StyleSheetDocument _getStyleSheetDocument(StyleContext context)
  {
    StyleSheetDocument document = _document;

    // If we have a StyleSheetDocument already, just return it.
    if (document != null)
      return document;

    // Otherwise, we create the StyleSheetDocument now
    // Note, it does not use the StyleContext. This is the StyleSheetDocument
    // for the entire skin document, so it includes all the specific rules
    // like @agent ie and @agent gecko, etc. It's later that we output
    // the css based on the StyleContext.
    document = createStyleSheetDocument(context);

    // If we weren't able to create the StyleSheetDocument,
    // use a non-null placeholder
    if (document == null)
      document = _EMPTY_DOCUMENT;

    // Save the document
    if (_document == null)
      _document = document;

    // Re-initialize our Array of namespace prefixes that are in the selectors
    // Re-initialize our Map of short style class names
    _namespacePrefixes = _getNamespacePrefixes(context, _document);
    _shortStyleClassMap = _getShortStyleClassMap(_document, _namespacePrefixes);

    return document;
  }

  /**
   * Returns an array of fully resolved StyleNodes for the
   * specified StyleContext  and StyleSheetDocument.
   * This will be those StyleNodes that match the locale, direction, browser, etc -- the
   * info that is in the StyleContext.
   */
  private StyleNode[] _getStyleContextResolvedStyles(
    StyleContext context,
    StyleSheetDocument document
    )
  {
    Iterator<StyleNode> e = document.getStyles(context);
    if ((e == null) || !e.hasNext())
    {
      if (_LOG.isWarning())
        _LOG.warning("NO_STYLES_FOUND_CONTEXT", context);
      return null;
    }

    List<StyleNode> v = new ArrayList<StyleNode>();
    while (e.hasNext())
      v.add(e.next());

    return v.toArray(new StyleNode[v.size()]);
  }

  /**
   * Returns a Map of skin property names to values for the specified
   * styleSheetNodes that have been filtered from the StyleContext and StyleSheetDocument.
   */
  private ConcurrentMap<Object, Object> _getStyleContextResolvedSkinProperties(
    StyleContext       context,
    StyleSheetDocument document
    )
  {
    Iterator<StyleSheetNode> styleSheetNodes = document.getStyleSheets(context);

    ConcurrentMap<Object, Object> skinProperties = new ConcurrentHashMap<Object, Object>();
    while (styleSheetNodes.hasNext())
    {
      StyleSheetNode styleSheetNode = styleSheetNodes.next();
      Collection<SkinPropertyNode> skinPropertyNodes = styleSheetNode.getSkinProperties();

      if (skinPropertyNodes != null)
      {
        for (SkinPropertyNode skinPropertyNode : skinPropertyNodes)
        {
          skinProperties.put(skinPropertyNode.getKey(), skinPropertyNode.getValue());
        }
      }
    }

    return skinProperties;
  }

  /**
   * Returns a Map of icon names to Icons for the specified
   * styleSheetNodes that have been filtered from the StyleContext and StyleSheetDocument.
   */
  private ConcurrentMap<String, Icon> _getStyleContextResolvedIcons(
    StyleContext       context,
    StyleSheetDocument document
    )
  {
    Iterator<StyleSheetNode> styleSheetNodes = document.getStyleSheets(context);

    ConcurrentMap<String, Icon> icons = new ConcurrentHashMap<String, Icon>();
    while (styleSheetNodes.hasNext())
    {
      StyleSheetNode styleSheetNode = styleSheetNodes.next();
      Collection<IconNode> iconNodes = styleSheetNode.getIcons();

      if (iconNodes != null)
      {
        for (IconNode iconNode : iconNodes)
        {
          icons.put(iconNode.getIconName(), iconNode.getIcon());
        }
      }
    }

    return icons;
  }

  /**
   * Generates the CSS files for the specified context and styles.
   * @return the names of the generated CSS files.
   */
  private List<String> _createStyleSheetFiles(
    StyleContext        context,
    StyleSheetDocument  document,
    StyleNode[]         styles,
    Map<String, String> shortStyleClassMap,
    String[]            namespacePrefixes,
    boolean             checkModified,
    boolean             isDirty)
  {

    // Get the current files
    List<File> outputFiles = _getOutputFiles(context, document);

    // If at least one output file exists, check the last modified time.
    if (!outputFiles.isEmpty())
    {
      // If the skin is marked dirty, we regenerate the css even if the document's timestamp has not
      // changed.
      if (checkModified || isDirty)
      {
        if (!isDirty && (checkModified && !_checkSourceModified(document, outputFiles.get(0))))
        {
          return _getFileNames(outputFiles);
        }
        // If the output file is older than the source file, we
        // need to regenerate the output file.  But first we
        // need to delete the old output file before we attempt to
        // create a new version.
        _deleteAll(outputFiles);
      }
      else
      {
        return _getFileNames(outputFiles);
      }
    }

    // Make sure the output directory exists in case it's been
    // blown away since the creation of the cache
    File outputDir = new File(_targetPath);
    if (!outputDir.exists())
      outputDir.mkdirs();

    // Write out the style sheet
    // First figure out whether or not we need to compress the style classes.
    // We don't compress the style classes if the content compression flag is disabled or
    // if the skin is a portlet skin.
    RenderingContext arc = RenderingContext.getCurrentInstance();
    Skin skin = arc.getSkin();
    boolean compressStyles = _isCompressStyles(context);

    StyleWriterFactoryImpl writerFactory = new StyleWriterFactoryImpl(_targetPath,
      getTargetStyleSheetName(context, document));
    CSSGenerationUtils.writeCSS(context,
                                skin.getStyleSheetName(),
                                styles,
                                writerFactory,
                                compressStyles,
                                shortStyleClassMap,
                                namespacePrefixes,
                                _STYLE_KEY_MAP
                                );

    writerFactory.close();

    // Return the name of the new style sheet
    return _getFileNames(writerFactory.getFiles());
  }

  private File _getOutputFile(String name, int number)
  {
    assert number >= 1;
    if (number == 1)
    {
      return new File(_targetPath, name);
    }
    int index = name.lastIndexOf(".");
    if (index < 0)
    {
      return new File(_targetPath, name + number);
    }
    else
    {
      // file name + number + file extension
      return new File(_targetPath,
        new StringBuilder(name.length() + 2).append(name.substring(0, index)).append(number)
          .append(name.substring(index)).toString());
    }
  }

  private void _deleteAll(Iterable<File> files)
  {
    for (File file : files)
    {
      if (file.exists())
      {
        boolean success = file.delete();
        // add warning if success is false, but continue on.
        // I've seen the delete fail when we try to delete right after the file was created -
        // like if the skin css file is modified and the page refreshed immediately after the
        // app was initially run.
        if (!success && _LOG.isWarning())
        {
          _LOG.warning("COULD_NOT_DELETE_FILE", file.getName());
        }
      }
    }
  }

  /**
   * First figure out whether or not we need to compress the style classes.
   * We don't compress the style classes if the content compression flag is disabled or
   * if the skin is a portlet skin.
   */
  private boolean _isCompressStyles(StyleContext sContext)
  {
    // if we are not disabling style compression, then we are compressing the styles
    return !(sContext.isDisableStyleCompression());

  }

  private List<String> _getFileNames(List<File> files)
  {
    List<String> names = new ArrayList<String>(files.size());
    for (File file : files)
    {
      names.add(file.getName());
    }
    return Collections.unmodifiableList(names);
  }

  /**
   * Returns the name of the output files that have been created for the given context and
   * document. If there are no files found, and empty list will be returned.
   * @return The list of list that currently exist, or null if none found
   */
  private List<File> _getOutputFiles(
    StyleContext context,
    StyleSheetDocument document
    )
  {
    // use a linked list as we only iterate and linked lists are faster for iteration & appending
    // than array lists.
    List<File> files = new LinkedList<File>();
    String name = getTargetStyleSheetName(context, document);
    for (int i = 1; true; ++i)
    {
      // we don't know in advance if there are any files, and if there are, how many of them
      // there are. Therefore, we keep incrementing the counter until the file doesn't exist and
      // at that point we know we have all of them
      File f = _getOutputFile(name, i);
      if (f.exists())
      {
        files.add(f);
      }
      else
      {
        break;
      }
    }

    return files;
  }

  /**
   * Returns the PrintWriter to use for the specified file
   */
  private PrintWriter _getWriter(File file)
  {
    PrintWriter out = null;

    try
    {
      File parentFile = file.getParentFile();
      if (parentFile != null)
        parentFile.mkdirs();

      // This throws a FileNotFoundException if it wasn't successfully deleted earlier, most likely
      // due to creating, then deleting too soon after.
      // Since the file has the hashcode in the name, it's not bad that it doesn't rewrite it.
      FileOutputStream fos = new FileOutputStream(file);
      OutputStreamWriter writer = null;

      // Use UTF8 encoding for output, in case font names have non-ascii
      // characters.
      try
      {
        writer = new OutputStreamWriter(fos, _UTF8_ENCODING);
      }
      catch (UnsupportedEncodingException e)
      {
        // UTF-8 should always be supported!
        assert false;

        // Just use default encoding instead
        writer = new OutputStreamWriter(fos);
      }

      out = new PrintWriter(new BufferedWriter(writer));
    }
    catch (IOException e)
    {
      // This might happen if we couldn't delete the css file that was already there, so we
      // are unable to recreate it.
      if (_LOG.isWarning())
        _LOG.warning("IOEXCEPTION_OPENNING_FILE", file);
        _LOG.warning(e);
    }

    return out;
  }

  /**
   * Checks to see whether the source file has been modified
   * since the specified output file was generated.  If so,
   * we need to regenerate the output file.
   */
  private boolean _checkSourceModified(
    StyleSheetDocument document,
    File               outputFile
    )
  {
    assert (outputFile != null);

    return (document.getDocumentTimestamp() > outputFile.lastModified());
  }

  /**
   * Create an array of all the namespace prefixes in the xss/css file. E.g., "af|" or "tr|"
   */
  private static String[] _getNamespacePrefixes(
    StyleContext       context,
    StyleSheetDocument document)
  {

    assert (document != null);
    Iterator<StyleSheetNode> styleSheets = document.getStyleSheets(context);
    assert (styleSheets != null);
    Set<String> namespacePrefixesSet = new HashSet<String>();
    while (styleSheets.hasNext())
    {
      StyleSheetNode styleSheet = styleSheets.next();
      Iterable<StyleNode> styles = styleSheet.getStyles();
      assert (styles != null);
      for (StyleNode style : styles)
      {
        String selector = style.getSelector();

        if (selector != null)
        {
          CSSGenerationUtils.getNamespacePrefixes(namespacePrefixesSet, selector);
        }
      }
    }
    return namespacePrefixesSet.toArray(_EMPTY_STRING_ARRAY);
  }

  /**
   * Create the map of full style classes to short style classes
   * Do not shorten styleclasses that start with SkinSelectors.STATE_PREFIX
   */
  private static Map<String, String> _getShortStyleClassMap(
    StyleSheetDocument document,
    String[]           namespacePrefixes)
  {
    // Use a HashMap to avoid unnecessary synchronization of Hashtable
    Map<String, String> map = new HashMap<String, String>();

    assert (document != null);
    // get all the styleSheets to create the shortened style class map
    // if we only got the ones based on the StyleContext, then we'd have
    // to create a shortened map for each StyleContext we receive and cache it.
    // it's more straightforward, faster, and requires less memory to get
    // all the styleSheets and create the shortened style class map. There might
    // be some styles in the map that have no properties in another StyleContext,
    // but that's ok. It doesn't hurt.
    Iterator<StyleSheetNode> styleSheets = document.getStyleSheets();
    assert (styleSheets != null);

    Set<String> emptySelectors = new HashSet<String>();
    Set<String> nonEmptySelectors = new HashSet<String>(512);

    while (styleSheets.hasNext())
    {
      StyleSheetNode styleSheet = styleSheets.next();
      Iterable<StyleNode> styles = styleSheet.getStyles();
      assert (styles != null);
      for (StyleNode style : styles)
      {
        String selector = style.getSelector();

        if (selector != null)
        {
          // If we've got a single style class selector, add it
          // to the map. Otherwise, we need to search the selector
          // for style classes.
          if (CSSGenerationUtils.isSingleStyleClassSelector(selector))
          {
            String styleClass = selector.substring(1);
            _putStyleClassInShortMap(styleClass, map);
            // don't shorten styleclasses that are states since they are likely to be added
            // and removed on the client.
            if (styleClass != null && !styleClass.startsWith(SkinSelectors.STATE_PREFIX))
              if (!map.containsKey(styleClass))
                map.put(styleClass, _getShortStyleClass(map.size()));

            if (style.isEmpty())
              emptySelectors.add(styleClass);
            else
              nonEmptySelectors.add(styleClass);
          }
          else
          {
            Iterator<String> styleClasses =
              CSSGenerationUtils.getStyleClasses(selector);

            if (styleClasses != null)
            {
              while (styleClasses.hasNext())
              {
                String styleClass = styleClasses.next();
                _putStyleClassInShortMap(styleClass, map);
                // Don't remove any styleclass that is referred to
                nonEmptySelectors.add(styleClass);
              }
            }

            // now search for the selectors that have namespaces and add those to the map
            int length = namespacePrefixes.length;

            for (int i=0; i < length; i++)
            {
              String nsPrefix = namespacePrefixes[i];
              Iterator<String> afSelectors =
                CSSGenerationUtils.getNamespacedSelectors(selector,
                                                          nsPrefix,
                                                          _STYLE_KEY_MAP);
              if (afSelectors != null)
              {
                boolean isFirst = true;
                while (afSelectors.hasNext())
                {
                  String styleClass = afSelectors.next();
                  _putStyleClassInShortMap(styleClass, map);
                  if (isFirst && !afSelectors.hasNext() && style.isEmpty())
                  {
                    emptySelectors.add(styleClass);
                  }
                  else
                  {
                    nonEmptySelectors.add(styleClass);
                  }

                  isFirst = false;
                }
              }
            }
          }
        }
      }
    }

    emptySelectors.removeAll(nonEmptySelectors);

    // Replace all empty keys with an empty string as the selector
    for (String emptyKey : emptySelectors)
      map.put(emptyKey, CoreRenderingContext.EMPTY_STYLE_CLASS);

    // We actually need a Map, since this object is exposed
    // via public APIs.  Also, we need the Map to be immutable,
    // or else we would need to create a new copy of the Map
    // each time it is requested.
    return Collections.unmodifiableMap(map);
  }

  /**
   * Method to put styleclasses in the shortened map.
   * We don't put 'state' styleclasses in the shortened map. Those are styleclasses
   * that start with SkinSelectors.STATE_PREFIX. The reason is that those
   * are likely to be added and removed on the client as the state changes, and
   * we don't want to require the shortened map on the client.
   */
  private static void _putStyleClassInShortMap(String styleClass, Map map)
  {
    if (styleClass != null &&
        !styleClass.startsWith(SkinSelectors.STATE_PREFIX) &&
        !map.containsKey(styleClass))
    {
      map.put(styleClass, _getShortStyleClass(map.size()));
    }
  }

  /**
   * Helper method used by _getShortStyleClassMap().  Returns a new
   * short style class selector.  The count is the number of style
   * classes seen so far.
   */
  private static String _getShortStyleClass(int count)
  {
    // At the moment the short style class is just based on the nubmer
    // of style classes and not the style class itself.
    return _SHORT_CLASS_PREFIX + Integer.toString(count, Character.MAX_RADIX);
  }


  /**
   * Key class used for hashing style sheet URIs
   */
  private static class Key
  {
    public Key(StyleContext context)
    {
      TrinidadAgent agent = context.getAgent();
      LocaleContext localeContext = context.getLocaleContext();
      AccessibilityProfile accProfile = context.getAccessibilityProfile();

      _init(
       localeContext.getTranslationLocale(),
       LocaleUtils.getReadingDirection(localeContext),
       agent.getAgentApplication(),
       agent.getAgentVersion(),
       agent.getAgentOS(),
       !context.isDisableStyleCompression(),
       accProfile,
       context.isPortletMode());
    }

    @Override
    public int hashCode()
    {
      if(_noHash)
      {
        //don't worry about synchronizing this
        _hashCode  =   (_direction)                ^
                       (_browser.ordinal() << 2)   ^
                       (_platform << 8)            ^
                       (_short ? 1 : 0)            ^
                       (_portlet ? 1:0);

        if (_locale != null)     _hashCode ^= _locale.hashCode();
        if (_accProfile != null) _hashCode ^= _accProfile.hashCode();
        if (_version != null)    _hashCode ^= (_version.hashCode());

        _noHash = false;
      }
      return _hashCode;
    }

    @Override
    public boolean equals(Object o)
    {
      if (this == o)
        return true;

      //Improved performance of this check
      if ((o.hashCode() == hashCode()) &&  (o instanceof Key))
      {
        Key key = (Key)o;
       
        // Check the easy stuff first
        if  ((_short == key._short)             &&
             (_portlet == key._portlet)         &&
             (_direction == key._direction)     &&
             (_browser == key._browser)         &&
             (_platform == key._platform))
        {
          // now check the optional objects
          if ((_version == null) || _version.equals(key._version))
            if ((_locale == null) || _locale.equals(key._locale))
              return ((_accProfile == null) || _accProfile.equals(key._accProfile));
        }
      }

      return false;
    }

    private void _init(
      Locale locale,
      int direction,
      TrinidadAgent.Application browser,
      String version,
      int platform,
      boolean useShort,
      AccessibilityProfile accessibilityProfile,
      boolean portlet
      )
    {
      // Make sure direction is non-null
      _locale = (locale == null) ? Locale.getDefault() : locale;

      // Make sure direction is not default
      _direction =
        (direction == LocaleUtils.DIRECTION_DEFAULT) ?
          LocaleUtils.getReadingDirectionForLocale(_locale) :
          direction;

      _browser = browser;
      _version = version;
      _platform = platform;
      _short = useShort;
      _accProfile = accessibilityProfile;
      _portlet     = portlet;
    }

    //is immutable, we should cache this, will make things faster in the long run
    private boolean        _noHash = true;
    private int            _hashCode;

    private Locale         _locale;
    private int            _direction;
    private TrinidadAgent.Application _browser;
    private String         _version;
    private int            _platform;
    private boolean        _short;  // Do we use short style classes?
    private AccessibilityProfile _accProfile;
    private boolean        _portlet; //kind of a hack but tells whether this was created in portal mode
  }

  /**
   * Cache entry class
   */
  private static class Entry
  {
    public final List<String> uris;
    public final Styles styles;
    public final ConcurrentMap<String, Icon> icons;
    public final ConcurrentMap<Object, Object> skinProperties;

    public Entry(
      List<String> uris,
      Styles styles,
      ConcurrentMap<String, Icon> icons,
      ConcurrentMap<Object, Object> skinProperties)
    {
      this.uris = uris;
      this.styles = styles;
      this.icons = icons;
      this.skinProperties = skinProperties;
    }
  }

  /**
   * A key object which is used to hash Entrys in the entry cache.  The key for the entry
   * cache is the style sheet derivation list - that is a list of StyleSheetNodes, sorted
   * by specficity.
   */
  private static class DerivationKey
  {
    public DerivationKey(StyleContext context, StyleSheetNode[] styleSheets)
    {
      _styleSheets = new StyleSheetNode[styleSheets.length];
      System.arraycopy(styleSheets, 0, _styleSheets, 0, styleSheets.length);
      _short = true;
      _portlet = context.isPortletMode();
    }

    @Override
    public boolean equals(Object o)
    {
      if (o == this)
        return true;

      if ((o.hashCode() == hashCode()) && (o instanceof DerivationKey))
      {
        DerivationKey key = (DerivationKey)o;

        if ((_short != key._short) ||
            (_portlet != key._portlet) ||
            (_styleSheets.length != key._styleSheets.length))
          return false;

        // Test each StyleSheetNode for equality.  We start
        // at the end of the list, as the last style sheet
        // is more likely to be different.  (The first entry
        // is probably the common base style sheet.)
        for (int i = _styleSheets.length - 1; i >= 0; i--)
        {
          if (!_styleSheets[i].equals(key._styleSheets[i]))
            return false;
        }

        return true;
      }

      return false;
    }

    @Override
    public int hashCode()
    {
      if(_noHash)
      {
        _hashCode = Arrays.hashCode(_styleSheets) ^
                    (_short ? 1 : 0)              ^
                    (_portlet ? 1 : 0);
        _noHash = false;
      }

      return _hashCode;
    }

    //This object is immutable. So we can cache the hashcode to make it faster
    private boolean _noHash = false;
    private int     _hashCode;

    private StyleSheetNode[] _styleSheets;
    private boolean _portlet;
    private boolean _short;   // Do we use short style classes?
  }




  /**
   * A Styles implementation that adds the resolved (merged together based on the StyleContext)
   * StyleNodes to a Map. Only the style selectors and not the aliased (aka named) styles
   * are added to this map.
   */
  private static final class StylesImpl extends Styles
  {
    /**
     * This constructor takes an array of StyleNode Objects where each StyleNode has
     * already been resolved based on the StyleContext. Therefore there is no
     * more merging that needs to be done, and the 'included' properties on
     * StyleNode are all null. This way we do not have to resolve the
     * styles based on the StyleContext when someone calls getStyles,
     * etc.
     * @param resolvedStyles
     * @param namespacePrefixArray an array of namespace prefixes that are used in the custom css
     * skinning selectors, like "af" in af|inputText.
     * @param afSelectorMap a map from one selector to another (like af|panelHeader::link maps to
     * af|panelHeader A
     * @param shortStyleClassMap a map from the  non-compressed styleclass
     * to a compressed styleclass.
     */
    public StylesImpl(
        StyleNode[]         resolvedStyles,
        String[]            namespacePrefixArray,
        Map<String, String> afSelectorMap,
        Map<String, String> shortStyleClassMap,
        boolean             compress
      )
    {
      // store these local variables to be used in getNativeSelectorString
      _namespacePrefixArray = namespacePrefixArray;
      _afSelectorMap = afSelectorMap;
      _shortStyleClassMap = shortStyleClassMap;
      _compress = compress;
      // create a Selector->Style map right here (aggressively versus lazily)
      ConcurrentMap<Selector, Style> resolvedSelectorStyleMap = null;


      // Loop through each StyleNode and use it to add to the StyleMap.
      for (int i=0; i < resolvedStyles.length; i++)
      {
        String selector = resolvedStyles[i].getSelector();
        if (selector != null)
        {
          Style style = _convertStyleNodeToStyle(resolvedStyles[i]);
          if (resolvedSelectorStyleMap == null)
            resolvedSelectorStyleMap = new ConcurrentHashMap<Selector, Style>();
          resolvedSelectorStyleMap.put(Selector.createSelector(selector), style);
        }
        /*
        else
        {
          // For now, do not add the named styles to the map. If we do add the named styles
          // to the map then we should in SkinStyleSheetParserUtils put the full name in
          // the StyleNode, not strip out the '.' or the ':alias'. However, in the XSS
          // the named styles do not have the '.' or the ':alias' which is why we string them out.
          // if we put them back, then things won't merge correctly. How do I workaround this?
          // Do I change all the named styles in the XSS file?
          // I think the best thing to do is to change the XSS file, since that is proprietary,
          // and no one should be relying on it. If we instead kept stripping out the . and the alias
          // and required the person to ask for the alias without this,
          // that is much more confusing to the user.
          String name = _resolvedStyles[i].getName();
          if (name != null)
          {
            // create a Style Object from the StyleNode object
            Style style = _convertStyleNode(resolvedStyles[i]);
            resolvedSelectorStyleMap.put(name, style);
          }
        }
        */
      }
      if (resolvedSelectorStyleMap != null)
        _unmodifiableResolvedSelectorStyleMap =
          Collections.unmodifiableMap(resolvedSelectorStyleMap);
      else
        _unmodifiableResolvedSelectorStyleMap = Collections.emptyMap();

    }

    /**
     * Returns a Map containing the selector String as the key and the Style Object
     * (contains all the css property names/values) as the value. This Map can then be used
     * to get all the selector keys, or to get the Style Object for a Selector, or to
     * get all the selectors that match some criteria, like they contain a certain simple selector.
     * This map does not contain 'alias' (aka named) selectors. It only contains selectors
     * that would be in the generated css file.
     *
     * @return unmodifiableMap of the resolved Selector -> Style map.
     */
    public Map<Selector, Style> getSelectorStyleMap()
    {
      return _unmodifiableResolvedSelectorStyleMap;
    }

    /**
     * Returns the Selector in String form, converted to a format that
     * is suitable to be written to the browse. This is the css-2 format which doesn't have
     * namespaces and our psuedo-elements.
     * @param selector Selector
     * @return String the Selector in a String form that is suitable to be
     * written to the client.
     */
    public String getNativeSelectorString(Selector selector)
    {
      // convert the selector to a valid css2 selector like the ones we write
      // to the generated css file.
      if (selector == null)
        throw new IllegalArgumentException("selector cannot be null");
      // run the selector through a conversion map so the selector is closer to
      // what we write out to the css. e.g., af|inputText:error::content becomes
      // af|inputText.p_AFError af|inputText::content. This way we don't have to
      // do this when we write the css inline. We have the information now.
      String mappedSelector =  CSSGenerationUtils.getMappedSelector(
        _afSelectorMap, _namespacePrefixArray, selector.toString());
      // run through the compressed map if it is compressed.
      if (_compress)
        mappedSelector  =
          CSSGenerationUtils.getShortSelector(_shortStyleClassMap,
                                              _namespacePrefixArray,
                                              mappedSelector);

      return CSSGenerationUtils.getValidFullNameSelector(
        mappedSelector, _namespacePrefixArray);
    }


    /**
     * Given a StyleNode object, which is an internal API that denotes a Style object
     * with additional information like includedSelectors, create a simple public
     * Style object which will be used in the SelectorStyleMap. The StyleNode object
     * should already be resolved (included selectors have been merged in)
     * so that all the css properties are there.
     * @param styleNode
     * @return A Style object created from the information in the styleNode.
     */
    public Style _convertStyleNodeToStyle(StyleNode styleNode)
    {
      Map<String, String> styleProperties = new ConcurrentHashMap<String, String>();
      // Add in the properties for the style
      Iterable<PropertyNode> propertyNodeList = styleNode.getProperties();
      for (PropertyNode property : propertyNodeList)
      {
        String name = property.getName();
        String value = property.getValue();
        // We get a NPE in CSSStyle(styleProperties) -> putAll if we add a value that is null.
        // See the BaseStyle(Map<String, String> propertiesMap) constructor.
        if (name != null && value != null)
          styleProperties.put(name, value);
      }

      return new CSSStyle(styleProperties);

    }

    private final Map<Selector, Style> _unmodifiableResolvedSelectorStyleMap;
    private final Map<String, String>  _afSelectorMap;
    private final String[]             _namespacePrefixArray;
    private final Map<String, String>  _shortStyleClassMap;
    private final boolean              _compress;
  }

  private class StyleWriterFactoryImpl
    implements StyleWriterFactory
  {
    private final String _outputDirectory;
    private final String _baseFilename;
    private PrintWriter _out;
    private List<File> _files = new LinkedList<File>();

    StyleWriterFactoryImpl(String outputDirectory, String baseName)
    {
      _outputDirectory = outputDirectory;
      _baseFilename = baseName;
    }

    List<File> getFiles()
    {
      return _files;
    }

    public PrintWriter createWriter()
    {
      if (_out != null)
      {
        _out.close();
      }

      File outputFile = _getOutputFile(_baseFilename, _files.size() + 1);
      // We never want to do anything other than read it or delete it:
      outputFile.setReadOnly();

      _files.add(outputFile);
      _out = _getWriter(outputFile);

      return _out;
    }

    void close()
    {
      if (_out != null)
      {
        _out.close();
        _out = null;
      }
    }
  }

  private final String _targetPath; // The location of the cache

  /** The parsed StyleSheetDocument */
  private StyleSheetDocument _document;

  /** The cache of style sheet URIs */
  private ConcurrentMap<Key, Entry> _cache;

  /**
   * We cache Entry objects, hashed by DerivationKey (ie.
   * hashed based on the StyleSheetNode derivation list).
   */
  private ConcurrentMap<Object, Entry> _entryCache;

  /** Map which maps from full style class names to our compressed names. */
  private Map<String, String> _shortStyleClassMap;
  private String[]            _namespacePrefixes;

  // Constants

  // Separator for variants in file names
  private static final char _NAME_SEPARATOR = '-';
  private static final String _COMPRESSED = "cmp";
  private static final String _PORTLET = "prtl";

  /** Extension for CSS files */
  private static final String _CSS_EXTENSION = ".css";

  /** Java name for UTF8 encoding */
  private static final String _UTF8_ENCODING = "UTF8";

  /** Stub StyleSheetDocument instance */
  private static final StyleSheetDocument _EMPTY_DOCUMENT =
    new StyleSheetDocument(null, null, StyleSheetDocument.UNKNOWN_TIMESTAMP);

  /** Prefix to use for short style classes */
  private static final String _SHORT_CLASS_PREFIX = "x";

  private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(FileSystemStyleCache.class);


  /**
   * use this map to map from the public style selector names to
   * our internal style selector names. The public style selector
   * names do not contain html, whereas our internal style selector
   * names may. We write out the shortened version of the mapped
   * selector names to the css file.
   * @todo Need to find a better spot for this, like the skin?
   */
  private static final Map<String, String> _STYLE_KEY_MAP;

  static
  {
    _STYLE_KEY_MAP =  new HashMap<String, String>();
    // we don't use a styleClass on tr:body. Instead we use the html element
    // BODY to style it. This makes it easier for users to use an external
    // stylesheet and not have to know our styleClass names.
    _STYLE_KEY_MAP.put("af|body", "BODY");

    _STYLE_KEY_MAP.put("af|panelHeader::level-one",
                 "H1.af|panelHeader");
    _STYLE_KEY_MAP.put("af|panelHeader::level-two",
                 "H2.af|panelHeader");
    _STYLE_KEY_MAP.put("af|panelHeader::level-three",
                 "H3.af|panelHeader");
    _STYLE_KEY_MAP.put("af|panelHeader::level-four",
                 "H4.af|panelHeader");
    _STYLE_KEY_MAP.put("af|panelHeader::level-five",
                 "H5.af|panelHeader");
    _STYLE_KEY_MAP.put("af|panelHeader::level-six",
                 "H6.af|panelHeader");

    // showDetailHeader
    _STYLE_KEY_MAP.put("af|showDetailHeader::level-one",
                 "H1.af|showDetailHeader");
    _STYLE_KEY_MAP.put("af|showDetailHeader::level-two",
                 "H2.af|showDetailHeader");
    _STYLE_KEY_MAP.put("af|showDetailHeader::level-three",
                 "H3.af|showDetailHeader");
    _STYLE_KEY_MAP.put("af|showDetailHeader::level-four",
                 "H4.af|showDetailHeader");
    _STYLE_KEY_MAP.put("af|showDetailHeader::level-five",
                 "H5.af|showDetailHeader");
    _STYLE_KEY_MAP.put("af|showDetailHeader::level-six",
                 "H6.af|showDetailHeader");

    _STYLE_KEY_MAP.put("af|menuTabs::selected-link",
                 "af|menuTabs::selected A");
    _STYLE_KEY_MAP.put("af|menuTabs::enabled-link",
                 "af|menuTabs::enabled A");

    _STYLE_KEY_MAP.put("af|menuBar::enabled-link",
                 "af|menuBar::enabled A");
    _STYLE_KEY_MAP.put("af|menuBar::selected-link",
                 "af|menuBar::selected A");

    _STYLE_KEY_MAP.put("OraLinkEnabledLink",
                 "OraLinkEnabled A:link");
    _STYLE_KEY_MAP.put("OraLinkSelectedLink",
                 "OraLinkSelected A:link");
    _STYLE_KEY_MAP.put("OraLinkEnabledActive",
                 "OraLinkEnabled A:active");
    _STYLE_KEY_MAP.put("OraLinkSelectedActive",
                 "OraLinkSelected A:active");
    _STYLE_KEY_MAP.put("OraLinkEnabledVisited",
                 "OraLinkEnabled A:visited");
    _STYLE_KEY_MAP.put("OraLinkSelectedVisited",
                 "OraLinkSelected A:visited");

    _STYLE_KEY_MAP.put("af|panelPage::about-link",
                 "af|panelPage::about A");
    _STYLE_KEY_MAP.put("af|panelPage::copyright-link",
                 "af|panelPage::copyright A");
    _STYLE_KEY_MAP.put("af|panelPage::privacy-link",
                 "af|panelPage::privacy A");

    _STYLE_KEY_MAP.put("af|panelList::link",
                 "af|panelList A");
    _STYLE_KEY_MAP.put("af|panelList::unordered-list",
                 "af|panelList UL");

    _STYLE_KEY_MAP.put("af|inputDate::nav-link",
                 "af|inputDate::nav A");
    _STYLE_KEY_MAP.put("af|inputDate::content-link",
                 "af|inputDate::content A");
    _STYLE_KEY_MAP.put("af|inputDate::disabled-link",
                 "af|inputDate::disabled A");
    _STYLE_KEY_MAP.put("af|chooseDate::nav-link",
                 "af|chooseDate::nav A");
    _STYLE_KEY_MAP.put("af|chooseDate::content-link",
                 "af|chooseDate::content A");

    _STYLE_KEY_MAP.put(
        SkinSelectors.AF_SHOWMANYACCORDION_TITLE_LINK_STYLE_CLASS,
        "A.af|showManyAccordion::title-link");
    _STYLE_KEY_MAP.put(
        SkinSelectors.AF_SHOWMANYACCORDION_TITLE_LINK_DISABLED_STYLE_CLASS,
        "A.af|showManyAccordion::title-disabled-link");

    _STYLE_KEY_MAP.put(
        SkinSelectors.AF_SHOWONEACCORDION_TITLE_LINK_STYLE_CLASS,
        "A.af|showOneAccordion::title-link");
    _STYLE_KEY_MAP.put(
        SkinSelectors.AF_SHOWONEACCORDION_TITLE_LINK_DISABLED_STYLE_CLASS,
        "A.af|showOneAccordion::title-disabled-link");

    _STYLE_KEY_MAP.put(
        SkinSelectors.AF_PANELACCORDION_TITLE_LINK_STYLE_CLASS,
        "A.af|panelAccordion::title-link");
    _STYLE_KEY_MAP.put(
        SkinSelectors.AF_PANELACCORDION_TITLE_LINK_DISABLED_STYLE_CLASS,
        "A.af|panelAccordion::title-disabled-link");

    _STYLE_KEY_MAP.put("af|panelTabbed::tab-link",
        "af|panelTabbed::tab A");
    _STYLE_KEY_MAP.put("af|panelTabbed::tab-selected-link",
        "af|panelTabbed::tab-selected A");

  }

  private static final StyleSheetNode[] _EMPTY_STYLE_SHEET_NODE_ARRAY = new StyleSheetNode[0];
  private static final String[] _EMPTY_STRING_ARRAY = new String[0];
}
TOP

Related Classes of org.apache.myfaces.trinidadinternal.style.cache.FileSystemStyleCache$Key

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.