1 /******************************************************************************* 2 * Copyright (c) 2008, 2018 Angelo Zerr and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation 13 * IBM Corporation - ongoing development 14 * Red Hat Inc. (mistria) - Fixes suggested by FindBugs 15 *******************************************************************************/ 16 package org.eclipse.e4.ui.css.core.dom.properties.providers; 17 18 import java.util.ArrayList; 19 import java.util.Collection; 20 import java.util.HashMap; 21 import java.util.List; 22 import java.util.Map; 23 import java.util.StringTokenizer; 24 import org.eclipse.e4.ui.css.core.dom.CSSStylableElement; 25 import org.eclipse.e4.ui.css.core.dom.properties.ICSSPropertyHandler; 26 import org.eclipse.e4.ui.css.core.engine.CSSEngine; 27 import org.eclipse.e4.ui.css.core.exceptions.UnsupportedClassCSSPropertyException; 28 import org.w3c.dom.css.CSSStyleDeclaration; 29 30 /** 31 * CSS property handler with lazy strategy. {@link ICSSPropertyHandler} are 32 * retrieved with name into packages registered with registerPackage method. 33 */ 34 public class CSSPropertyHandlerLazyProviderImpl extends 35 AbstractCSSPropertyHandlerProvider { 36 37 // List of package names containing handlers class for properties 38 private List<String> packageNames = new ArrayList<>(); 39 40 // Map used as a cache for properties handlers found 41 private Map<String, List<ICSSPropertyHandler>> propertyToHandlersMap = new HashMap<>(); 42 43 /** 44 * Return the list of PropertiesHandler corresponding to the property name 45 * given as argument 46 */ 47 @Override getCSSPropertyHandlers( String property)48 public Collection<ICSSPropertyHandler> getCSSPropertyHandlers( 49 String property) throws Exception { 50 51 // Test if ICSSPropertyHandler List was stored into cache 52 // with key property 53 Map<String, List<ICSSPropertyHandler>> propertyHandlers = getPropertyToHandlersMap(); 54 if (propertyHandlers.containsKey(property)) { 55 return propertyHandlers.get(property); 56 } 57 58 List<ICSSPropertyHandler> handlers = null; 59 try { 60 String handlerClassName = getHandlerClassName(property); 61 for (String packageName : packageNames) { 62 ICSSPropertyHandler handler = getCSSPropertyHandler( 63 packageName, handlerClassName); 64 if (handler != null) { 65 //TODO replace with eclipse logging 66 // if (logger.isDebugEnabled()) 67 // logger.debug("Handle CSS Property=" + property 68 // + ", with class=" + packageName + "." 69 // + handlerClassName); 70 if (handlers == null) { 71 handlers = new ArrayList<>(); 72 } 73 handlers.add(handler); 74 } 75 } 76 //TODO replace with eclipse logging 77 // if (logger.isDebugEnabled()) { 78 // if (handlers == null) 79 // logger.debug("Cannot find Handle Class CSS Property=" 80 // + property + ", for class=" + handlerClassName); 81 // } 82 } finally { 83 propertyHandlers.put(property, handlers); 84 } 85 return handlers; 86 } 87 88 /** 89 * Register a package path "name.name1." where to search for PropertyHandler 90 * class 91 * 92 * @param packageName 93 */ registerPackage(String packageName)94 public void registerPackage(String packageName) { 95 packageNames.add(packageName); 96 propertyToHandlersMap = null; 97 } 98 getPropertyToHandlersMap()99 protected Map<String, List<ICSSPropertyHandler>> getPropertyToHandlersMap() { 100 if (propertyToHandlersMap == null) { 101 propertyToHandlersMap = new HashMap<>(); 102 } 103 return propertyToHandlersMap; 104 } 105 106 /** 107 * Reflexive method that return a property handler class 108 * 109 * @param packageName 110 * @param handlerClassName 111 * @return 112 * @throws Exception 113 */ getCSSPropertyHandler(String packageName, String handlerClassName)114 protected ICSSPropertyHandler getCSSPropertyHandler(String packageName, 115 String handlerClassName) throws Exception { 116 String handlerClass = packageName + "." + handlerClassName; 117 try { 118 Class<?> clazz = this.getClass().getClassLoader() 119 .loadClass( 120 handlerClass); 121 Object instance = clazz.getDeclaredConstructor().newInstance(); 122 if (!(instance instanceof ICSSPropertyHandler)) { 123 throw new UnsupportedClassCSSPropertyException(clazz); 124 } 125 return (ICSSPropertyHandler) clazz.getDeclaredConstructor().newInstance(); 126 } catch (ClassNotFoundException e) { 127 128 } 129 return null; 130 } 131 132 /** 133 * Return the handler class name corresponding to the property label given 134 * as argument A Property Handler Class Name is CSSPropertyXXXHandler (like 135 * CSSPropertyBorderTopColorHandler) 136 * 137 * @param property 138 * @return 139 */ getHandlerClassName(String property)140 protected String getHandlerClassName(String property) { 141 StringBuilder handlerClassName = new StringBuilder("CSSProperty"); //$NON-NLS-1$ 142 for (StringTokenizer t = new StringTokenizer(property, "-"); t.hasMoreTokens();) { 143 String p = t.nextToken(); 144 handlerClassName.append(p.substring(0, 1).toUpperCase()); 145 handlerClassName.append(p.substring(1)); 146 } 147 handlerClassName.append("Handler"); //$NON-NLS-1$ 148 return handlerClassName.toString(); 149 } 150 151 @Override getDefaultCSSStyleDeclaration( CSSEngine engine, CSSStylableElement stylableElement, CSSStyleDeclaration newStyle, String pseudoE)152 protected CSSStyleDeclaration getDefaultCSSStyleDeclaration( 153 CSSEngine engine, CSSStylableElement stylableElement, 154 CSSStyleDeclaration newStyle, String pseudoE) throws Exception { 155 if (stylableElement.getDefaultStyleDeclaration(pseudoE) != null) { 156 return stylableElement.getDefaultStyleDeclaration(pseudoE); 157 } 158 if (newStyle != null) { 159 StringBuilder style = null; 160 int length = newStyle.getLength(); 161 for (int i = 0; i < length; i++) { 162 String propertyName = newStyle.item(i); 163 String[] compositePropertiesNames = engine 164 .getCSSCompositePropertiesNames(propertyName); 165 if (compositePropertiesNames != null) { 166 for (String compositePropertyName : compositePropertiesNames) { 167 propertyName = compositePropertyName; 168 String s = getCSSPropertyStyle(engine, stylableElement, 169 propertyName, pseudoE); 170 if (s != null) { 171 if (style == null) { 172 style = new StringBuilder(); 173 } 174 style.append(s); 175 } 176 } 177 } else { 178 String s = getCSSPropertyStyle(engine, stylableElement, 179 propertyName, pseudoE); 180 if (s != null) { 181 if (style == null) { 182 style = new StringBuilder(); 183 } 184 style.append(s); 185 } 186 } 187 } 188 if (style != null) { 189 CSSStyleDeclaration defaultStyleDeclaration = engine 190 .parseStyleDeclaration(style.toString()); 191 stylableElement.setDefaultStyleDeclaration(pseudoE, 192 defaultStyleDeclaration); 193 return defaultStyleDeclaration; 194 } 195 } 196 return stylableElement.getDefaultStyleDeclaration(pseudoE); 197 } 198 199 @Override getCSSPropertyHandlers( Object element, String property)200 public Collection<ICSSPropertyHandler> getCSSPropertyHandlers( 201 Object element, String property) throws Exception { 202 return getCSSPropertyHandlers(property); 203 } 204 205 @Override getCSSProperties(Object element)206 public Collection<String> getCSSProperties(Object element) { 207 Map<String, List<ICSSPropertyHandler>> propertyHandlers = getPropertyToHandlersMap(); 208 // FIXME: could walk the package names, look for the classes matching 209 // the class pattern and go from there 210 return propertyHandlers.keySet(); 211 } 212 213 } 214