From adffaces-commits-return-2333-apmail-incubator-adffaces-commits-archive=incubator.apache.org@incubator.apache.org Tue Apr 17 00:08:23 2007 Return-Path: Delivered-To: apmail-incubator-adffaces-commits-archive@locus.apache.org Received: (qmail 77931 invoked from network); 17 Apr 2007 00:08:23 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 17 Apr 2007 00:08:23 -0000 Received: (qmail 78989 invoked by uid 500); 17 Apr 2007 00:08:29 -0000 Delivered-To: apmail-incubator-adffaces-commits-archive@incubator.apache.org Received: (qmail 78977 invoked by uid 500); 17 Apr 2007 00:08:29 -0000 Mailing-List: contact adffaces-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: adffaces-dev@incubator.apache.org Delivered-To: mailing list adffaces-commits@incubator.apache.org Received: (qmail 78968 invoked by uid 99); 17 Apr 2007 00:08:29 -0000 Received: from herse.apache.org (HELO herse.apache.org) (140.211.11.133) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 16 Apr 2007 17:08:29 -0700 X-ASF-Spam-Status: No, hits=-99.5 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 16 Apr 2007 17:08:21 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id 67AA61A9838; Mon, 16 Apr 2007 17:08:01 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r529450 - in /incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style: cache/FileSystemStyleCache.java xml/parse/StyleSheetDocument.java Date: Tue, 17 Apr 2007 00:08:00 -0000 To: adffaces-commits@incubator.apache.org From: awiner@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20070417000801.67AA61A9838@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: awiner Date: Mon Apr 16 17:07:56 2007 New Revision: 529450 URL: http://svn.apache.org/viewvc?view=rev&rev=529450 Log: ADFFACES-394: Style sheet processing optimization Processing the Trinidad minimal stylesheet is now about 93% faster (50ms -> 3.6ms). On a much larger stylesheet, timings improved from 440ms to 10ms (97.5% improvement). Two big optimizations: - Before parsing, cache maps from name -> StyleNode and selector -> StyleNode. Implemented this cache in a new StyleSheetList inner class. - Instead of looping through gathered styles to see if we've found a duplicate, maintain Sets of gathered names and selectors. Minor optimization: - Move the "put()" calls on the cache of resolved styles from getStyles() down to _resolveStyle(). And also removed some unused code. Modified: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java Modified: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java?view=diff&rev=529450&r1=529449&r2=529450 ============================================================================== --- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java (original) +++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/cache/FileSystemStyleCache.java Mon Apr 16 17:07:56 2007 @@ -579,7 +579,7 @@ ) { Iterator e = document.getStyles(context); - if (e == null) + if ((e == null) || !e.hasNext()) { if (_LOG.isWarning()) _LOG.warning("No styles found context - " + context); Modified: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java?view=diff&rev=529450&r1=529449&r2=529450 ============================================================================== --- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java (original) +++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/xml/parse/StyleSheetDocument.java Mon Apr 16 17:07:56 2007 @@ -20,32 +20,31 @@ import java.awt.Color; -import java.util.Map; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; import java.util.Stack; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.List; - -import org.apache.myfaces.trinidadinternal.util.nls.LocaleUtils; -import java.util.Comparator; - -import org.apache.myfaces.trinidad.util.IntegerUtils; +import org.apache.myfaces.trinidad.context.LocaleContext; import org.apache.myfaces.trinidad.logging.TrinidadLogger; +import org.apache.myfaces.trinidad.util.IntegerUtils; import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent; -import org.apache.myfaces.trinidad.context.LocaleContext; - import org.apache.myfaces.trinidadinternal.style.PropertyParseException; -import org.apache.myfaces.trinidadinternal.style.Style; import org.apache.myfaces.trinidadinternal.style.StyleContext; import org.apache.myfaces.trinidadinternal.style.util.CSSUtils; import org.apache.myfaces.trinidadinternal.style.util.ModeUtils; import org.apache.myfaces.trinidadinternal.style.util.NameUtils; +import org.apache.myfaces.trinidadinternal.util.nls.LocaleUtils; /** @@ -88,7 +87,7 @@ * @param styleSheets The StyleSheetNodes which define the contents * of this StyleSheetDocument. * @param documentVersion The version identifier for this StyleSheetDocument. - * @param timestamp The timestamp for this StyleSheetDocument. + * @param documentTimestamp The timestamp for this StyleSheetDocument. */ public StyleSheetDocument( StyleSheetNode[] styleSheets, @@ -175,11 +174,8 @@ */ public Iterator getStyleSheets(StyleContext context) { - StyleSheetNode[] styleSheets = _getStyleSheets(context); - if(styleSheets == null) - return (Arrays.asList(new StyleSheetNode[0])).iterator(); - else - return (Arrays.asList(styleSheets)).iterator(); + StyleSheetList styleSheets = _getStyleSheets(context); + return styleSheets.styleSheets().iterator(); } /** @@ -188,10 +184,12 @@ @SuppressWarnings("unchecked") public Iterator getStyles(StyleContext context) { - // Get the matching style sheets, including the UserStyleSheet - StyleSheetNode[] styleSheets = _getStyleSheets(context); - if (styleSheets == null) - return EmptyIterator.getInstance(); + StyleSheetList styleSheets = _getStyleSheets(context); + if (styleSheets.isEmpty()) + { + List emptyList = Collections.emptyList(); + return emptyList.iterator(); + } // We are going to loop through every StyleNode in every StyleSheetNode, // resolving each one along the way. We store resolved StyleNodes in @@ -207,69 +205,68 @@ HashMap resolvedNamedStyles = new HashMap(); + // Keep track of all selectors and names that we've already + // found, so that we don't bother re-resolving them + // These differ from the above Maps in that the Maps are + // mutated by _resolveStyle - so an included selector, for + // instance, might already be present in _resolveStyle but + // not yet have been encountered by this top loop. We + // could eliminate the need for this if we were willing + // to give up the ordering of the list + Set foundSelectors = new HashSet(); + Set foundNames = new HashSet(); + // Now, loop through all StyleNodes in all StyleSheetNodes - // Note: The algorithm used here is actually much more inefficient - // than it needs to be. We're using a n-squared algorithm here - - // for every style we find, we need to loop through all other styles. - // The reason for this is because this allows us to share code - // (namely _resolveStyle()) with getStyleBySelector/Name(). However, - // we could probably double the performance of CSS generation by - // optimizing this routine to make a single pass through all styles, - // and then resolve everything in one step. (Currently, it takes - // about 100ms to generate a CSS version of blaf.xss - we could reduce - // this to about 50ms if needed.) - for (int i = 0; i < styleSheets.length; i++) + + // We want to cache IDs in this use of StyleSheetList + styleSheets.cacheIds(); + + for (StyleSheetNode styleSheet : styleSheets.styleSheets()) { - StyleSheetNode styleSheet = styleSheets[i]; Iterable styleNodeList = styleSheet.getStyles(); for (StyleNode node : styleNodeList) { - String id = null; - boolean isNamed = false; + String id; + boolean isFound; + boolean isNamed; + // Is this a named node or a selector? if (node.getName() != null) { - id = node.getName(); isNamed = true; + id = node.getName(); + isFound = foundNames.contains(id); } else { + isNamed = false; id = node.getSelector(); + isFound = foundSelectors.contains(id); } - StyleNode resolvedNode = _resolveStyle(context, - styleSheets, - resolvedStyles, - resolvedNamedStyles, - null, - null, - id, - isNamed); - - // If we got a node, add it in to our list - if (resolvedNode != null) + // If we've already seen that node/selector, no need to look + // for it again + if (!isFound) { + StyleNode resolvedNode = _resolveStyle(context, + styleSheets, + resolvedStyles, + resolvedNamedStyles, + null, + null, + id, + isNamed); - // cache already resolved styles so we don't - // resolve them again. This saves time. - - String namedStyle = resolvedNode.getName(); - String selectorStyle = resolvedNode.getSelector(); - if (namedStyle != null) - { - resolvedNamedStyles.put(namedStyle, resolvedNode); - } - else if (selectorStyle != null) + // If we got a node, add it in to our list + if (resolvedNode != null) { - resolvedStyles.put(selectorStyle, resolvedNode); - } - - - // add it to our list - - if (!_containsStyle(styles, resolvedNode)) styles.add(resolvedNode); + if (isNamed) + foundNames.add(id); + else + foundSelectors.add(id); + } } } @@ -303,7 +300,7 @@ } // Returns array of matching style sheets sorted by specificity - private StyleSheetNode[] _getStyleSheets( + private StyleSheetList _getStyleSheets( StyleContext context ) { @@ -327,7 +324,7 @@ int count = v.size(); if (count == 0) - return null; + return new StyleSheetList(null); // Sort the matching style sheets by specificity StyleSheetNode[] styleSheets = v.toArray(new StyleSheetNode[count]); @@ -340,7 +337,7 @@ Arrays.sort(styleSheets, comparator); - return styleSheets; + return new StyleSheetList(styleSheets); } // Gets the style with the specified selector/name @@ -350,8 +347,8 @@ boolean isNamed ) { - StyleSheetNode[] styleSheets = _getStyleSheets(context); - if (styleSheets == null) + StyleSheetList styleSheets = _getStyleSheets(context); + if (styleSheets.isEmpty()) return null; return _resolveStyle(context, @@ -386,7 +383,7 @@ */ private StyleNode _resolveStyle( StyleContext context, - StyleSheetNode[] styleSheets, + StyleSheetList styleSheets, Map resolvedStyles, Map resolvedNamedStyles, Stack includesStack, @@ -420,6 +417,8 @@ // If we've already got a style, return it and we're done! if (style != null) { + // FIXME: AdamWiner - _ERROR_STYLE_NODE is in fact never used! + // We use _ERROR_STYLE_NODE for internal error tracking, but we // never return it - return null instead. if (style == _ERROR_STYLE_NODE) @@ -461,126 +460,117 @@ includesStack.push(id); } - // Now, we loop through all of the StyleSheetNodes and collect - // properties defined by matching StyleNodes. We resolve any - // included styles along the way. - for (int i = 0; i < styleSheets.length; i++) + List nodeList = styleSheets.styleNodes(id, isNamed); + if (nodeList != null) { - Iterable nodes = styleSheets[i].getStyles(); - - for (StyleNode node : nodes) + for (StyleNode node : nodeList) { - - if ((isNamed && name.equals(node.getName())) || - (!isNamed && selector.equals(node.getSelector()))) + // We've got a match! We need to do the following: + // 0. Check to see whether we need to reset our properties. + // 1. Resolve any included styles, and shove those properties + // into our StyleEntry. + // 2. Resolve any included properties, and shove those properties + // into our StyleEntry. + // 3. Remove all properties that were inhibited. + // 4. Shove all properties from the matching StyleNode into our + // StyleEntry, overwriting included values + // -= Simon Lessard =- + // FIXME: That sequence looks buggy. If more than 1 matching node + // is found, then the included properties of the second will + // have priority over the properties found at step 5 on the + // first node, which is most likely incorrect. + // + // A possible fix would be to put entries from the 5 steps + // into 5 different lists then resolve all priorities at the + // end. + + // 0. Reset properties? + if (node.__getResetProperties() || node.isInhibitingAll()) + entry.resetProperties(); + + // 1. Resolve included styles + Iterable includedStyles = node.getIncludedStyles(); + for (IncludeStyleNode includeStyle : includedStyles) { - // We've got a match! We need to do the following: - // 0. Check to see whether we need to reset our properties. - // 1. Resolve any included styles, and shove those properties - // into our StyleEntry. - // 2. Resolve any included properties, and shove those properties - // into our StyleEntry. - // 3. Remove all properties that were inhibited. - // 4. Shove all properties from the matching StyleNode into our - // StyleEntry, overwriting included values - // -= Simon Lessard =- - // FIXME: That sequence looks buggy. If more than 1 matching node - // is found, then the included properties of the second will - // have priority over the properties found at step 5 on the - // first node, which is most likely incorrect. - // - // A possible fix would be to put entries from the 5 steps - // into 5 different lists then resolve all priorities at the - // end. - - // 0. Reset properties? - if (node.__getResetProperties() || node.isInhibitingAll()) - entry.resetProperties(); - - // 1. Resolve included styles - Iterable includedStyles = node.getIncludedStyles(); - for (IncludeStyleNode includeStyle : includedStyles) - { - String includeID = null; - boolean includeIsNamed = false; - - if (includeStyle.getName() != null) - { - includeID = includeStyle.getName(); - includeIsNamed = true; - } - else - { - includeID = includeStyle.getSelector(); - } - - StyleNode resolvedNode = _resolveStyle(context, - styleSheets, - resolvedStyles, - resolvedNamedStyles, - includesStack, - namedIncludesStack, - includeID, - includeIsNamed); + String includeID = null; + boolean includeIsNamed = false; - if (resolvedNode != null) - _addIncludedProperties(entry, resolvedNode); + if (includeStyle.getName() != null) + { + includeID = includeStyle.getName(); + includeIsNamed = true; + } + else + { + includeID = includeStyle.getSelector(); } + StyleNode resolvedNode = _resolveStyle(context, + styleSheets, + resolvedStyles, + resolvedNamedStyles, + includesStack, + namedIncludesStack, + includeID, + includeIsNamed); - // 2. Resolve included properties - Iterable includedProperties = node.getIncludedProperties(); - for (IncludePropertyNode includeProperty : includedProperties) + if (resolvedNode != null) + _addIncludedProperties(entry, resolvedNode); + } + + + // 2. Resolve included properties + Iterable includedProperties = node.getIncludedProperties(); + for (IncludePropertyNode includeProperty : includedProperties) + { + String includeID = null; + boolean includeIsNamed = false; + + if (includeProperty.getName() != null) { - String includeID = null; - boolean includeIsNamed = false; - - if (includeProperty.getName() != null) - { - includeID = includeProperty.getName(); - includeIsNamed = true; - } - else - { - includeID = includeProperty.getSelector(); - } - - StyleNode resolvedNode = _resolveStyle(context, - styleSheets, - resolvedStyles, - resolvedNamedStyles, - includesStack, - namedIncludesStack, - includeID, - includeIsNamed); - - if (resolvedNode != null) - { - _addIncludedProperty(entry, - resolvedNode, - includeProperty.getPropertyName(), - includeProperty.getLocalPropertyName()); - } + includeID = includeProperty.getName(); + includeIsNamed = true; } - - // 3. Check inhibited properties - Iterable inhibitedProperties = node.getInhibitedProperties(); - for (String inhibitedPropertyName : inhibitedProperties) + else { - entry.removeProperty(inhibitedPropertyName); + includeID = includeProperty.getSelector(); } - - - // 4. Add non-included properties - Iterable properties = node.getProperties(); - for (PropertyNode propertyNode : properties) + + StyleNode resolvedNode = _resolveStyle(context, + styleSheets, + resolvedStyles, + resolvedNamedStyles, + includesStack, + namedIncludesStack, + includeID, + includeIsNamed); + + if (resolvedNode != null) { - entry.addProperty(propertyNode); + _addIncludedProperty(entry, + resolvedNode, + includeProperty.getPropertyName(), + includeProperty.getLocalPropertyName()); } } + + // 3. Check inhibited properties + Iterable inhibitedProperties = node.getInhibitedProperties(); + for (String inhibitedPropertyName : inhibitedProperties) + { + entry.removeProperty(inhibitedPropertyName); + } + + + // 4. Add non-included properties + Iterable properties = node.getProperties(); + for (PropertyNode propertyNode : properties) + { + entry.addProperty(propertyNode); + } } } - + // Pop the include stack if (isNamed) { @@ -591,8 +581,35 @@ includesStack.pop(); } + StyleNode resolvedNode = entry.toStyleNode(); + + // If we got a node, add it in to our list + if (resolvedNode != null) + { + // cache already resolved styles so we don't + // resolve them again. This saves (a lot of) time. + // TODO: AdamWiner: entry.toStyleNode() will return + // null if it's an empty style. But that means + // that this cache doesn't get used, so we end + // up hammering on these. This doesn't appear + // to be a performance issue at this time. + String namedStyle = resolvedNode.getName(); + if (namedStyle != null) + { + resolvedNamedStyles.put(namedStyle, resolvedNode); + } + else + { + String selectorStyle = resolvedNode.getSelector(); + if (selectorStyle != null) + { + resolvedStyles.put(selectorStyle, resolvedNode); + } + } + } + // Convert the StyleEntry to a StyleNode and return it - return entry.toStyleNode(); + return resolvedNode; } // Adds all of the properties from the StyleNode into the StyleEntry @@ -701,45 +718,6 @@ return new PropertyNode(_FONT_SIZE_NAME, newValue); } - // Returns the value of the property with the specified name - private String _getPropertyValue(StyleNode style, String propertyName) - { - Iterable properties = style.getProperties(); - for (PropertyNode property : properties) - { - if (propertyName.equals(property.getName())) - return property.getValue(); - } - - return null; - } - - - // Creates a StyleNode for the specified Style. The key is either a - // selector or a name, depending on the value of the isNamed parameter. - private StyleNode _createStyleNode(String key, Style style, boolean isNamed) - { - // Covert the properties into PropertyNodes - List v = new ArrayList(); - Iterator names = style.getPropertyNames(); - - while (names.hasNext()) - { - String name = (String)names.next(); - String value = style.getProperty(name); - - v.add(new PropertyNode(name, value)); - } - - PropertyNode[] nodes = v.toArray(new PropertyNode[v.size()]); - - if (isNamed) - { - return new StyleNode(key, null, nodes, null, null, null); - } - - return new StyleNode(null, key, nodes, null, null, null); - } // Tests whether the value is present in the (possibly null) stack. private static boolean _stackContains(Stack stack, Object value) @@ -750,32 +728,136 @@ return stack.contains(value); } - // Tests whether the value is present in the (possibly null) stack. - private static boolean _containsStyle(List v, StyleNode node) + /** + * Simple encapsulation of an array of StyleSheetNodes, to + * support maintaining a cache of (name | selector --> StyleNode) + */ + private static class StyleSheetList { - String id = null; - boolean isNamed = false; - - if (node.getName() != null) + public StyleSheetList(StyleSheetNode[] styleSheets) { - id = node.getName(); - isNamed = true; + _styleSheets = styleSheets; } - else - { - id = node.getSelector(); + + /** + * @return true if the list is empty + */ + public boolean isEmpty() + { + return (_styleSheets == null) || (_styleSheets.length == 0); + } + + /** + * Forcibly caches IDs. Caching IDs is only useful if + * we're going to be retrieving a lot (generally, all) + * of the nodes at some point. + */ + public void cacheIds() + { + // Typically, there are a lot more selector nodes than + // name nodes. These numbers come from some statistics gathered + // on Trinidad and other libraries + _nameNodes = new HashMap>(256); + _selectorNodes = new HashMap>(1024); + + for (int i = 0; i < _styleSheets.length; i++) + { + StyleSheetNode styleSheet = _styleSheets[i]; + Iterable styleNodeList = styleSheet.getStyles(); + + for (StyleNode node : styleNodeList) + { + if (node.getName() != null) + _addToMap(_nameNodes, node, node.getName()); + else + _addToMap(_selectorNodes, node, node.getSelector()); + } + } + } + + /** + * Return a List of StyleNodes based on the "id" (either + * a selector or name) + * @param id the selector or name + * @param isNamed if true, interpret "id" as a name, otherwise + * as a selector + * @return the list of StyleNodes (potentially null) + */ + public List styleNodes(String id, boolean isNamed) + { + if (_styleSheets == null) + return Collections.emptyList(); + Map> m = isNamed ? _nameNodes : _selectorNodes; + // Cached version - go to the Map + if (m != null) + return m.get(id); + + // Uncached version - iterate through everything and build up + // the List + List l = new ArrayList(); + for (int i = 0; i < _styleSheets.length; i++) + { + StyleSheetNode styleSheet = _styleSheets[i]; + Iterable styleNodeList = styleSheet.getStyles(); + + for (StyleNode node : styleNodeList) + { + if (isNamed) + { + if (id.equals(node.getName())) + l.add(node); + } + else + { + if (id.equals(node.getSelector())) + l.add(node); + } + } + } + + return l; } - for(StyleNode otherNode : v) + /** + * @return an unmodifiable list of all StyleSheetNodes + */ + public List styleSheets() { - if ((isNamed && id.equals(otherNode.getName())) || - (!isNamed && id.equals(otherNode.getSelector()))) - return true; + if (_styleSheets == null) + { + return Collections.emptyList(); + } + else + { + return Collections.unmodifiableList(Arrays.asList(_styleSheets)); + } } - return false; - } + static private void _addToMap( + Map> m, + StyleNode node, + String id) + { + List l = m.get(id); + if (l == null) + { + // Most properties, in fact, have only one entry + // The most I've seen is 8. This could probably + // be reduced to 2 + l = new ArrayList(4); + m.put(id, l); + } + + l.add(node); + } + + private Map> _nameNodes; + private Map> _selectorNodes; + + private final StyleSheetNode[] _styleSheets; + } + // Comparator for StyleSheetNodes which sorts by variant specificity private static class StyleSheetComparator implements Comparator { @@ -995,27 +1077,6 @@ return new StyleNode(name, selector, properties, null, null, null); } - // Tests whether a property with the specified name is - // contained within the List of PropertyNodes - // -= Simon Lessard =- - // FIXME: Never used locally as of 2006-08-04 - @SuppressWarnings("unused") - private boolean _containsProperty( - ArrayList properties, - String name) - { - if (properties == null) - return false; - - for(PropertyNode property : properties) - { - if ((property != null) && (name.equals(property.getName()))) - return true; - } - - return false; - } - // Removes the PropertyNode with the specified name from the // List of properties. Note - we assume that the properties // List will contain at most one property with the specified @@ -1289,38 +1350,6 @@ // The next non-null value in the wrapped enumeration private T _next; } - - // Iterator implementation for empty set - private static class EmptyIterator implements Iterator - { - private EmptyIterator() {} - - public static Iterator getInstance() - { - if (_sInstance == null) - _sInstance = new EmptyIterator(); - - return _sInstance; - } - - public boolean hasNext() - { - return false; - } - - public T next() - { - throw new NoSuchElementException(); - } - - public void remove() - { - throw new UnsupportedOperationException(); - } - - private static Iterator _sInstance; - } - // A silly Iterator which converts "font-size" PropertyNodes to // absolute values, using a specified relative font size