// at this point the styles StyleNode[] can contain both Styles with
// non-null selector or non-null name(aka alias). We only generate
// the styles where getSelector is non-null.
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();
if (!compressStyles)
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++;
}
}
if (compressStyles)
{
String shortSelector =
getShortSelector(shortStyleClassMap, namespacePrefixArray, mappedSelectors[j]);
// 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();