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