Bug 553022 - Optimize property handler lists for results of size 0 and 1

The CSS Engine calls
RegistryCSSPropertyHandlerProvider#getCSSPropertyHandlers for each
element and style
property during application of theme styles. The resulting collection is
normally empty or has one property handler. The method created an
ArrayList for each call, which is wasting resources.

With this change an empty list is used as long as no handler has to be
returned. When the first handler is added a singleton list is returned.
These singleton lists are cached as the amount of property handlers is
limited.

When a second handler is added, an ArrayList is instantiated and this
and further handlers added to it.

The same optimization is applied for
AbstractCSSEngine#applyStyleDeclaration. Also here 'handlers2' is
usually empty or contains 1 handler.

As a result, invocations of these method won't produce garbage.

Change-Id: I11da2e55fe451799499c2f70725ddee5308f3273
Signed-off-by: Karsten Thoms <karsten.thoms@itemis.de>
diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/AbstractCSSEngine.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/AbstractCSSEngine.java
index 0cd41bab..4e8d9cc 100644
--- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/AbstractCSSEngine.java
+++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/AbstractCSSEngine.java
@@ -29,6 +29,7 @@
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -144,6 +145,8 @@
 	 * An ordered list of ICSSPropertyHandlerProvider
 	 */
 	protected List<ICSSPropertyHandlerProvider> propertyHandlerProviders = new ArrayList<>();
+	// for performance hold a map of handlers to singleton list
+	private Map<ICSSPropertyHandler2, List<ICSSPropertyHandler2>> propertyHandler2InstanceMap = new HashMap<>();
 
 	private Map<String, String> currentCSSPropertiesApplied;
 
@@ -541,7 +544,7 @@
 		if (avoidanceCacheInstalled) {
 			currentCSSPropertiesApplied = new HashMap<>();
 		}
-		List<ICSSPropertyHandler2> handlers2 = null;
+		List<ICSSPropertyHandler2> handlers2 = Collections.emptyList();
 		for (int i = 0; i < style.getLength(); i++) {
 			String property = style.item(i);
 			CSSValue value = style.getPropertyCSSValue(property);
@@ -550,17 +553,23 @@
 				ICSSPropertyHandler2 propertyHandler2 = null;
 				if (handler instanceof ICSSPropertyHandler2) {
 					propertyHandler2 = (ICSSPropertyHandler2) handler;
-				} else {
-					if (handler instanceof ICSSPropertyHandler2Delegate) {
-						propertyHandler2 = ((ICSSPropertyHandler2Delegate) handler).getCSSPropertyHandler2();
-					}
+				} else if (handler instanceof ICSSPropertyHandler2Delegate) {
+					propertyHandler2 = ((ICSSPropertyHandler2Delegate) handler).getCSSPropertyHandler2();
 				}
 				if (propertyHandler2 != null) {
-					if (handlers2 == null) {
-						handlers2 = new ArrayList<>();
-					}
-					if (!handlers2.contains(propertyHandler2)) {
+					switch (handlers2.size()) {
+					case 0:
+						handlers2 = propertyHandler2InstanceMap.computeIfAbsent(propertyHandler2,
+								h -> Collections.singletonList(h));
+						break;
+					case 1:
+						handlers2 = new ArrayList<>(handlers2);
 						handlers2.add(propertyHandler2);
+						break;
+					default:
+						if (!handlers2.contains(propertyHandler2)) {
+							handlers2.add(propertyHandler2);
+						}
 					}
 				}
 			} catch (Exception e) {
@@ -569,13 +578,11 @@
 				}
 			}
 		}
-		if (handlers2 != null) {
-			for (ICSSPropertyHandler2 handler2 : handlers2) {
-				try {
-					handler2.onAllCSSPropertiesApplyed(element, this, pseudo);
-				} catch (Exception e) {
-					handleExceptions(e);
-				}
+		for (ICSSPropertyHandler2 handler2 : handlers2) {
+			try {
+				handler2.onAllCSSPropertiesApplyed(element, this, pseudo);
+			} catch (Exception e) {
+				handleExceptions(e);
 			}
 		}
 		if (avoidanceCacheInstalled) {
diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/RegistryCSSPropertyHandlerProvider.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/RegistryCSSPropertyHandlerProvider.java
index 628bc78..0b22932 100644
--- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/RegistryCSSPropertyHandlerProvider.java
+++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/RegistryCSSPropertyHandlerProvider.java
@@ -17,6 +17,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -53,6 +54,8 @@
 	private boolean hasDeprecatedProperties = false; // mild optimization for getCSSProperties()
 
 	private Map<String, Map<String, ICSSPropertyHandler>> propertyHandlerMap = new HashMap<>();
+	// for performance hold a map of handlers to singleton list
+	private Map<ICSSPropertyHandler, List<ICSSPropertyHandler>> propertyHandlerInstanceMap = new HashMap<>();
 
 	public RegistryCSSPropertyHandlerProvider(IExtensionRegistry registry) {
 		this.registry = registry;
@@ -181,13 +184,22 @@
 
 	@Override
 	public Collection<ICSSPropertyHandler> getCSSPropertyHandlers(Object element, String property) throws Exception {
-		List<ICSSPropertyHandler> handlers = new ArrayList<>();
+		List<ICSSPropertyHandler> handlers = Collections.emptyList();
 		Class<?> clazz = element.getClass();
 		while (clazz != Object.class) {
 			if (propertyHandlerMap.containsKey(clazz.getName())) {
 				ICSSPropertyHandler handler = propertyHandlerMap.get(clazz.getName()).get(property);
 				if (handler != null) {
-					handlers.add(handler);
+					switch (handlers.size()) {
+					case 0:
+						handlers = propertyHandlerInstanceMap.computeIfAbsent(handler,
+								h -> Collections.singletonList(h));
+						break;
+					case 1:
+						handlers = new ArrayList<>(handlers);
+					default:
+						handlers.add(handler);
+					}
 				}
 			}
 			clazz = clazz.getSuperclass();