// pass to find matching StyleNodes.
String[] propertyStrings = new String[styles.length];
for (int i = 0; i < styles.length; i++)
{
StyleNode style = styles[i];
if (style.getSelector() != null)
{
// Get the property string (properties are sorted so that
// the order doesn't affect whether styles match).
String propertyString = _getSortedPropertyString(style);
// See if we already have a StyleNode with the same properties
StyleNode[] matchingStyles = matchingStylesMap.get(propertyString);
if (matchingStyles == null)
{
// If we don't already have matching StyleNodes, add this
// StyleNode to the map.
propertyStrings[i] = propertyString;
matchingStyles = new StyleNode[1];
matchingStyles[0] = style;
}
else
{
// If we already have matching StyleNodes, add this StyleNode
// to the end of the list of matching StyleNodes.
int length = matchingStyles.length;
StyleNode[] newMatchingStyles = new StyleNode[length + 1];
System.arraycopy(matchingStyles,
0,
newMatchingStyles,
0,
length);
newMatchingStyles[length] = style;
matchingStyles = newMatchingStyles;
}
// Rehash with the new value
matchingStylesMap.put(propertyString, matchingStyles);
}
}
// We'll start writing the CSS file now. First
// write out the header with a time stamp
Date date = new Date();
out.println("/* This CSS file generated on " + date + " */");
// Keep track of the number of selectors written out. The reason? IE has a 4095 limit,
// and we want to warn when we get to that limit.
int numberSelectorsWritten = 0;
// This is the second pass in which we write out the style rules
// Get the baseURI up front so we don't have to recalculate it every time we find
// a property value that contains url() and need to resolve the uri.
String baseURI = CSSUtils.getBaseSkinStyleSheetURI(styleSheetName);
for (int i = 0; i < styles.length; i++)
{
StyleNode style = styles[i];
String propertyString = propertyStrings[i];
// We only write out styles for which we have a property string.
// All other entries correspond to styles which don't have selectors -
// or styles which will be rendered as a "matching" style.
if (propertyString != null)
{
// Get all of the styles which share this property string.
StyleNode[] matchingStyles = matchingStylesMap.get(propertyString);
// Actually, we should always have at least one StyleNode here
assert (matchingStyles != null);
// determine if the current CSS file can fit all of the CSS selectors, or if a new
// one will be needed.
// TODO: figure out why we write both the uncompressed & compressed styles for styles
// without a '|' character, shouldn't the uncompressed be enough on its own? This results
// in some ugly code here
int stylesToBeWritten = 0;
String[] selectors = new String[matchingStyles.length];
String[] mappedSelectors = new String[matchingStyles.length];
for (int j = 0; j < matchingStyles.length; j++)
{
selectors[j] = matchingStyles[j].getSelector();
// We should always have a selector at this point
assert (selectors[j] != null);
mappedSelectors[j] = _getMappedSelector(afSelectorMap,
namespacePrefixArray,
selectors[j]);
if (compressStyles && (mappedSelectors[j].indexOf('|') == -1))
{
stylesToBeWritten += 2;
}
else
{
stylesToBeWritten++;
}
}
if (numberSelectorsWritten + matchingStyles.length >= _MSIE_SELECTOR_LIMIT
&& TrinidadAgent.APPLICATION_IEXPLORER == context.getAgent().getAgentApplication())
{
out.println("/* The number of CSS selectors in this file is " +
numberSelectorsWritten + " */");
out = writerFactory.createWriter();
if (out == null)
{
return;
}
numberSelectorsWritten = 0;
}
// Write out all of the style selectors for this property string
for (int j = 0; j < matchingStyles.length; j++)
{
String validFullNameSelector = null;
// write out the full selector if we aren't compressing styles or
// it doesn't have a '|' in the name which means it may be a user's public styleclass
// and we don't want to compress those; we will also write out the compressed
// version the public styleclasses in the next step.
if (!compressStyles || (mappedSelectors[j].indexOf('|') == -1))
{
validFullNameSelector =
_getValidFullNameSelector(mappedSelectors[j], namespacePrefixArray);
if (validFullNameSelector != null)
{
out.print(validFullNameSelector);
numberSelectorsWritten++;
}
}
// shorten all the css-2 style class selectors (those that start with
// '.' and don't have a namespace prefix in it)
// and return the shortened string.
// e.g., selector of '.OraBulletedList A' is shortened to '.xj A'
// e.g., selector of af|inputText::content is not shortened since
// it has no css-2 style class selector piece that starts with '.'.
// e.g., selector of af|foo.Bar will shorten the '.Bar' piece
// af|foo.xz
// e.g., .Foo:hover -> .x0:hover
if (compressStyles)
{
String shortSelector = _getShortSelector(mappedSelectors[j],
shortStyleClassMap);
if (shortSelector == null)
shortSelector = mappedSelectors[j];
// run it through a shortener one more time to shorten any
// of the af component selectors.
// e.g., 'af|menuPath' is shortened to '.x11'
if (_hasNamespacePrefix(shortSelector, namespacePrefixArray))
{
String[] shortSelectorArray = _splitStringByWhitespace(shortSelector);
shortSelector =
_getMappedNSSelector(shortStyleClassMap,
namespacePrefixArray,
shortSelector,
shortSelectorArray,
true);
}
// if the transformed full name is different than the shortSelector
// then write out the shortSelector, too.
if (shortSelector != null)
{
String validShortSelector =
_getValidFullNameSelector(shortSelector, namespacePrefixArray);
// if we wrote out a full style, check to see if we need to write out the short, too.
// if it is something different, write out the short, too.
if (validFullNameSelector != null)
{
//Since validFullNameSelector is not null, we know we wrote out a full style
// we write out a short style too in this case if it is different
// example: .PublicStyleClass is written out fully even in compressed mode, but
// it is different in compressed mode, so we write that out, too.
if (!validFullNameSelector.equals(validShortSelector))
{
out.print(',');
out.print(validShortSelector);
numberSelectorsWritten++;
}
}
else
{
out.print(validShortSelector);
numberSelectorsWritten++;
}
}
}
// Write out a separator between matching selectors
if (j < (matchingStyles.length - 1))
out.print(",");
}
// Now that we have written out the selectors, write out
// the properties
out.print(" {");
// At this point, we could just write out the property string
// that we already created, but this string contains the properties
// in sorted order. We prefer to attempt to preserve the order
// of the properties as specified in the XSS document. So,
// we get the properties from the StyleNode object instead of
// using the propertyString
Iterable<PropertyNode> properties = style.getProperties();
boolean first = true;
for (PropertyNode property : properties)
{
String propName = property.getName();