All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jaxx.compiler.css.StylesheetHelper Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-6
Show newest version
/*
 * #%L
 * JAXX :: Compiler
 * %%
 * Copyright (C) 2008 - 2014 Code Lutin, Tony Chemit
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */

package jaxx.compiler.css;

import jaxx.compiler.CompiledObject;
import jaxx.compiler.CompilerException;
import jaxx.compiler.JAXXCompiler;
import jaxx.compiler.binding.DataBinding;
import jaxx.compiler.binding.DataBindingHelper;
import jaxx.compiler.binding.PseudoClassDataBinding;
import jaxx.compiler.css.parser.CSSParser;
import jaxx.compiler.css.parser.CSSParserConstants;
import jaxx.compiler.css.parser.CSSParserTreeConstants;
import jaxx.compiler.css.parser.SimpleNode;
import jaxx.compiler.reflect.ClassDescriptor;
import jaxx.compiler.reflect.ClassDescriptorHelper;
import jaxx.compiler.reflect.MethodDescriptor;
import jaxx.compiler.tags.DefaultComponentHandler;
import jaxx.compiler.tags.DefaultObjectHandler;
import jaxx.compiler.tags.TagManager;
import jaxx.compiler.types.TypeManager;
import jaxx.runtime.css.Pseudoclasses;
import jaxx.runtime.css.Rule;
import jaxx.runtime.css.Selector;
import jaxx.runtime.css.Stylesheet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * A helper class to compute {@link Stylesheet}, {@link Rule} and {@link Selector}
 * and extract all the compiler logic from this class.
 * 

* In that way we can make the compiler as a single module and a runtime as another module. * * @author Tony Chemit - [email protected] */ public class StylesheetHelper { /** Logger */ static private final Log log = LogFactory.getLog(StylesheetHelper.class); public static Stylesheet processStylesheet(String stylesheetText) throws CompilerException { CSSParser p = new CSSParser(new StringReader(stylesheetText)); SimpleNode node; try { node = p.Stylesheet(); } catch (Error e) { throw new CompilerException(e); } List rules = new ArrayList(); for (int i = 0; i < node.jjtGetNumChildren(); i++) { SimpleNode ruleNode = node.getChild(i); Rule rule = processRule(ruleNode); rules.add(rule); } Stylesheet stylesheet; stylesheet = new Stylesheet(rules.toArray(new Rule[rules.size()])); return stylesheet; } public static Rule processRule(SimpleNode ruleNode) { if (ruleNode.getId() != CSSParserTreeConstants.JJTRULE) { throw new IllegalArgumentException("argument node is not a Rule"); } SimpleNode selectorsNode = ruleNode.getChild(0); assert selectorsNode.getId() == CSSParserTreeConstants.JJTSELECTORS : "expected node to be of type Selectors"; List selectors = new ArrayList(); for (int i = 0; i < selectorsNode.jjtGetNumChildren(); i++) { SimpleNode selectorNode = selectorsNode.getChild(i); selectors.add(processSelector(selectorNode)); } Map properties = new HashMap(); for (int i = 1; i < ruleNode.jjtGetNumChildren(); i++) { SimpleNode declarationNode = ruleNode.getChild(i); if (declarationNode.getId() == CSSParserTreeConstants.JJTDECLARATION) { String key = declarationNode.getChild(0).getText(); SimpleNode valueNode = declarationNode.getChild(1); String value = valueNode.getText(); if (valueNode.firstToken.kind == CSSParserConstants.STRING) { value = value.substring(1, value.length() - 1); } properties.put(key, value); } } Rule rule; rule = new Rule(selectors.toArray( new Selector[selectors.size()]), properties); return rule; } public static Selector processSelector(SimpleNode selector) { if (selector.getId() != CSSParserTreeConstants.JJTSELECTOR) { throw new IllegalArgumentException("argument node is not a Selector"); } String javaClassName = null; String styleClass = null; String pseudoClass = null; String id = null; for (int i = 0; i < selector.jjtGetNumChildren(); i++) { SimpleNode child = selector.getChild(i); switch (child.getId()) { case CSSParserTreeConstants.JJTJAVACLASS: if (!child.getText().trim().equals("*")) { javaClassName = child.getText(); } break; case CSSParserTreeConstants.JJTCLASS: styleClass = child.getText().substring(1); break; case CSSParserTreeConstants.JJTPSEUDOCLASS: pseudoClass = child.getText().substring(1); break; case CSSParserTreeConstants.JJTID: id = child.getText().substring(1); break; default: throw new IllegalStateException( "unexpected child of Selector node, type=" + child.getId()); } } return new Selector(javaClassName, styleClass, pseudoClass, id); } public enum MouseEventEnum { mouseover("mouseEntered", "mouseExited"), mouseout("mouseExited", "mouseReleased"), mousedown("mousePressed", "mousePressed"), mouseup("mouseReleased", "mousePressed"); final String addMethod; final String removeMethod; // ClassDescriptor mouseListenerDescriptor; // ClassDescriptor mouseEventDescriptor; MouseEventEnum(String addMethod, String removeMethod) { this.removeMethod = removeMethod; this.addMethod = addMethod; } public String getProperty(int i) { return i == 0 ? addMethod : removeMethod; } } static ClassDescriptor mouseListenerDescriptor; static ClassDescriptor mouseEventDescriptor; public static ClassDescriptor getMouseEventDescriptor() { if (mouseEventDescriptor == null) { mouseEventDescriptor = ClassDescriptorHelper.getClassDescriptor(MouseEvent.class); } return mouseEventDescriptor; } public static ClassDescriptor getMouseListenerDescriptor() { if (mouseListenerDescriptor == null) { mouseListenerDescriptor = ClassDescriptorHelper.getClassDescriptor(MouseListener.class); } return mouseListenerDescriptor; } public static MethodDescriptor getAddMouseListenerMethod(CompiledObject object) { try { MethodDescriptor addMouseListener = object.getObjectClass().getMethodDescriptor( "addMouseListener", getMouseListenerDescriptor() ); return addMouseListener; } catch (NoSuchMethodException e) { throw new CompilerException( "could not find addMouseListener for object " + object); } } public static MethodDescriptor getMouseListenerMethod(CompiledObject object, String property) { try { MethodDescriptor methodDescriptor = getMouseListenerDescriptor().getMethodDescriptor( property, getMouseEventDescriptor() ); return methodDescriptor; } catch (NoSuchMethodException e) { throw new CompilerException( "could not find " + property + " for object " + object); } } public static void applyTo(CompiledObject object, JAXXCompiler compiler, Stylesheet stylesheet, Stylesheet overrides) throws CompilerException { Map overriddenProperties; if (overrides != null) { overriddenProperties = getApplicableProperties(overrides, object); //overriddenProperties = overrides.getApplicableProperties(s,object); } else { overriddenProperties = null; } Map properties = getApplicableProperties(stylesheet, object); if (properties != null) { if (overriddenProperties != null) { properties.keySet().removeAll(overriddenProperties.keySet()); } DefaultObjectHandler handler = TagManager.getTagHandler(object.getObjectClass()); for (Map.Entry e : properties.entrySet()) { String value = e.getValue(); if (value.equals(Rule.INLINE_ATTRIBUTE) || value.equals(Rule.DATA_BINDING)) { continue; } if (handler instanceof DefaultComponentHandler) handler.setAttribute(object, e.getKey(), e.getValue(), false, compiler); else { handler.setAttributeFromCss(object, e.getKey(), value, compiler); } } } Rule[] pseudoClasses = getApplicablePseudoClasses(stylesheet, object); if (pseudoClasses != null) { Map> combinedPseudoClasses = new LinkedHashMap>(); for (Rule pseudoClass1 : pseudoClasses) { Selector[] selectors = pseudoClass1.getSelectors(); for (Selector selector : selectors) { if (appliesTo(selector, object) == Selector.PSEUDOCLASS_APPLIES) { properties = pseudoClass1.getProperties(); String pseudoClass = selector.getPseudoClass(); // TODO: overrides by downstream pseudoclasses are not handled Map combinedProperties = combinedPseudoClasses.get(pseudoClass); if (combinedProperties == null) { combinedProperties = new HashMap(); combinedPseudoClasses.put(pseudoClass, combinedProperties); } combinedProperties.putAll(properties); } } } int count = 0; for (Map.Entry> e : combinedPseudoClasses.entrySet()) { applyPseudoClass(e.getKey(), e.getValue(), object, compiler, count++); } } } public static String unwrap(ClassDescriptor type, String valueCode) { if (ClassDescriptorHelper.getClassDescriptor(boolean.class).equals(type)) { return "((Boolean) " + valueCode + ").booleanValue()"; } if (ClassDescriptorHelper.getClassDescriptor(byte.class).equals(type)) { return "((Byte) " + valueCode + ").byteValue()"; } if (ClassDescriptorHelper.getClassDescriptor(short.class).equals(type)) { return "((Short) " + valueCode + ").shortValue()"; } if (ClassDescriptorHelper.getClassDescriptor(int.class).equals(type)) { return "((Integer) " + valueCode + ").intValue()"; } if (ClassDescriptorHelper.getClassDescriptor(long.class).equals(type)) { return "((Long) " + valueCode + ").longValue()"; } if (ClassDescriptorHelper.getClassDescriptor(float.class).equals(type)) { return "((Float) " + valueCode + ").floatValue()"; } if (ClassDescriptorHelper.getClassDescriptor(double.class).equals(type)) { return "((Double) " + valueCode + ").doubleValue()"; } if (ClassDescriptorHelper.getClassDescriptor(char.class).equals(type)) { return "((Character) " + valueCode + ").charValue()"; } return valueCode; } public enum PseudoClassEnum { focused("{ object.hasFocus() }"), unfocused("{ !object.hasFocus() }"), enabled("{ object.isEnabled() }"), disabled("{ !object.isEnabled() }"), selected("{ object.isSelected() }"), deselected("{ !object.isSelected() }"); final String code; PseudoClassEnum(String code) { this.code = code; } public String getCode() { return code; } } public static void applyPseudoClass(String pseudoClass, Map properties, CompiledObject object, JAXXCompiler compiler, int priority) throws CompilerException { if (pseudoClass.contains("[")) { pseudoClass = pseudoClass.substring(0, pseudoClass.indexOf("[")); } StringBuilder buffer = new StringBuilder(); DefaultObjectHandler handler = TagManager.getTagHandler(object.getObjectClass()); boolean valueDeclared = false; String eol = JAXXCompiler.getLineSeparator(); DataBindingHelper bindingHelper = compiler.getBindingHelper(); String pseudoClassesPrefix = null; String dataBindingPrefix = null; if (!properties.isEmpty()) { pseudoClassesPrefix = compiler.getImportedType(Pseudoclasses.class); dataBindingPrefix = compiler.getImportedType(jaxx.runtime.css.DataBinding.class); } String outputClassName = compiler.getImportedType(compiler.getOutputClassName()); for (Map.Entry e : properties.entrySet()) { String property = e.getKey(); ClassDescriptor type = handler.getPropertyType(object, property, compiler ); if (log.isDebugEnabled()) { log.debug("will test if databinding : [" + e.getValue() + "] type=" + type); } String dataBindingCode = DataBindingHelper.processDataBindings(e.getValue()); String valueCode; String simpleType = compiler.getImportedType(JAXXCompiler.getCanonicalName(type)); if (dataBindingCode != null) { String code = object.getId() + "." + property + "." + priority; valueCode = "new " + dataBindingPrefix + "(" + TypeManager.getJavaCode(code) + ")"; DataBinding binding = new DataBinding( code, dataBindingCode, handler.getSetPropertyCode( object.getJavaCode(), property, "(" + simpleType + ") " + dataBindingCode, // "(" + JAXXCompiler.getCanonicalName(type) + ") " + dataBindingCode, compiler ), false ); bindingHelper.registerDataBinding(binding); } else { try { Class typeClass = type != null ? ClassDescriptorHelper.getClass( type.getName(), type.getClassLoader() ) : null; valueCode = TypeManager.getJavaCode( TypeManager.convertFromString(e.getValue(), typeClass) ); } catch (ClassNotFoundException ex) { compiler.reportError( "could not find class " + type.getName()); return; } } if (!valueDeclared) { buffer.append("Object "); valueDeclared = true; } buffer.append("value = "); buffer.append(pseudoClassesPrefix); buffer.append(".applyProperty("); buffer.append(outputClassName); buffer.append(".this, "); buffer.append(object.getJavaCode()); buffer.append(", "); buffer.append(TypeManager.getJavaCode(property)); buffer.append(", "); buffer.append(valueCode); buffer.append(", "); buffer.append(pseudoClassesPrefix); buffer.append(".wrap("); buffer.append(handler.getGetPropertyCode(object.getJavaCode(), property, compiler)); buffer.append("), "); buffer.append(priority); buffer.append(");"); buffer.append(eol); buffer.append("if (!(value instanceof "); buffer.append(dataBindingPrefix); buffer.append(")) {"); buffer.append(eol); String unwrappedValue = unwrap(type, "value"); buffer.append(" "); buffer.append(handler.getSetPropertyCode(object.getJavaCode(), property, "(" + simpleType + ") " + unwrappedValue, compiler)); buffer.append(eol); buffer.append("}").append(eol); } try { PseudoClassEnum classEnum = PseudoClassEnum.valueOf(pseudoClass); pseudoClass = classEnum.getCode(); } catch (IllegalArgumentException e) { // should never happens ? // throw new RuntimeException("could not find " + PseudoClassEnum.class + " with pseudoClass " + pseudoClass, e); } compilePseudoClass(pseudoClass, object, buffer.toString(), 0, "add", compiler, false); buffer.setLength(0); valueDeclared = false; for (Map.Entry e : properties.entrySet()) { String property = e.getKey(); ClassDescriptor type = handler.getPropertyType(object, property, compiler); String simpleType = compiler.getImportedType(JAXXCompiler.getCanonicalName(type)); if (log.isDebugEnabled()) { log.debug("will test if databinding : [" + e.getValue() + "] type=" + type); } String dataBindingCode = DataBindingHelper.processDataBindings(e.getValue()); String valueCode; if (dataBindingCode != null) { String code = object.getId() + "." + property + "." + priority; valueCode = "new " + dataBindingPrefix + "(" + TypeManager.getJavaCode(code) + ")"; DataBinding binding = new DataBinding( code, dataBindingCode, handler.getSetPropertyCode( object.getJavaCode(), property, "(" + simpleType + ") " + dataBindingCode, compiler ), false ); bindingHelper.registerDataBinding(binding); } else { try { Class typeClass = type != null ? ClassDescriptorHelper.getClass(type.getName(), type.getClassLoader()) : null; valueCode = TypeManager.getJavaCode(TypeManager.convertFromString(e.getValue(), typeClass)); } catch (ClassNotFoundException ex) { compiler.reportError("could not find class " + type.getName()); return; } } if (!valueDeclared) { buffer.append("Object "); valueDeclared = true; } buffer.append("value = ").append(pseudoClassesPrefix).append(".removeProperty("); buffer.append(outputClassName); buffer.append(".this, "); buffer.append(object.getJavaCode()); buffer.append(", "); buffer.append(TypeManager.getJavaCode(property)); buffer.append(", "); buffer.append(valueCode); buffer.append(", ").append(pseudoClassesPrefix).append(".wrap("); buffer.append(handler.getGetPropertyCode(object.getJavaCode(), property, compiler) ); buffer.append("), "); buffer.append(priority); buffer.append(");"); buffer.append(eol); buffer.append("if (!(value instanceof "); buffer.append(dataBindingPrefix); buffer.append(")) {"); buffer.append(eol); // String simpleType = importManager.getType(JAXXCompiler.getCanonicalName(type)); String unwrappedValue = unwrap(type, "value"); buffer.append(" "); buffer.append(handler.getSetPropertyCode( object.getJavaCode(), property, "(" + simpleType + ") " + unwrappedValue, compiler) ); buffer.append(eol); buffer.append("}").append(eol); } compilePseudoClass(pseudoClass, object, buffer.toString(), 1, "remove", compiler, true ); } public static void compilePseudoClass(String pseudoClass, CompiledObject object, String propertyCode, int pos, String methodName, JAXXCompiler compiler, boolean invertTest) throws CompilerException { PseudoClassDataBinding binding = PseudoClassDataBinding.newPseudoClassDataBinding( pseudoClass, object, propertyCode, methodName, invertTest ); if (binding != null) { compiler.getBindingHelper().registerDataBinding(binding); return; } MouseEventEnum constant = MouseEventEnum.valueOf(pseudoClass); String property = constant.getProperty(pos); MethodDescriptor addMouseListener = getAddMouseListenerMethod(object); MethodDescriptor methodDescriptor = getMouseListenerMethod(object, property); object.addEventHandler("style." + pseudoClass + "." + methodName, addMouseListener, methodDescriptor, propertyCode, compiler ); } public static Map getApplicableProperties( Stylesheet s, CompiledObject object) throws CompilerException { DefaultObjectHandler handler = TagManager.getTagHandler(object.getObjectClass()); Map result = null; for (Rule rule : s.getRules()) { int apply = appliesTo(rule, object); if (apply == Selector.ALWAYS_APPLIES || apply == Selector.ALWAYS_APPLIES_INHERIT_ONLY) { if (result == null) { result = new HashMap(); } for (Map.Entry entry : rule.getProperties().entrySet()) { String property = entry.getKey(); if (apply == Selector.ALWAYS_APPLIES || handler.isPropertyInherited(property)) { result.put(property, entry.getValue()); } } } } return result; } public static Rule[] getApplicablePseudoClasses( Stylesheet s, CompiledObject object) throws CompilerException { List result = null; for (Rule rule : s.getRules()) { if (appliesTo(rule, object) == Selector.PSEUDOCLASS_APPLIES) { if (result == null) { result = new ArrayList(); } result.add(rule); } } return result != null ? result.toArray(new Rule[result.size()]) : null; } public static Rule inlineAttribute(CompiledObject object, String propertyName, boolean dataBinding) { Map properties = new HashMap(); properties.put(propertyName, dataBinding ? Rule.DATA_BINDING : Rule.INLINE_ATTRIBUTE); return new Rule(new Selector[]{ new Selector(null, null, null, object.getId(), true)}, properties ); } public static int appliesTo(Rule rule, CompiledObject object) throws CompilerException { int appliesTo = Selector.NEVER_APPLIES; for (Selector selector : rule.getSelectors()) { appliesTo = Math.max(appliesTo(selector, object), appliesTo); if (appliesTo == Selector.ALWAYS_APPLIES || appliesTo == Selector.ALWAYS_APPLIES_INHERIT_ONLY) { break; } } return appliesTo; } public static int appliesTo(Selector selector, CompiledObject object) { boolean inheritOnly = false; CompiledObject parent = object; String javaClassName = selector.getJavaClassName(); String styleClass = selector.getStyleClass(); String pseudoClass = selector.getPseudoClass(); String id = selector.getId(); while (parent != null) { boolean classMatch = javaClassName == null; if (!classMatch) { ClassDescriptor javaClass = parent.getObjectClass(); do { String name = javaClass.getName(); if (name.equals(javaClassName) || name.substring(name.lastIndexOf(".") + 1).equals(javaClassName)) { classMatch = true; break; } javaClass = javaClass.getSuperclass(); } while (javaClass != null); } boolean styleClassMatch = styleClass == null || styleClass.equals(parent.getStyleClass()); String objectId = parent.getId(); objectId = objectId.substring(objectId.lastIndexOf(".") + 1); boolean idMatch = id == null || (' ' + objectId + ' ').contains(' ' + id + ' '); if (classMatch && styleClassMatch && idMatch) { if (pseudoClass != null) { return inheritOnly ? Selector.PSEUDOCLASS_APPLIES_INHERIT_ONLY : Selector.PSEUDOCLASS_APPLIES; } else { return inheritOnly ? Selector.ALWAYS_APPLIES_INHERIT_ONLY : Selector.ALWAYS_APPLIES; } } parent = parent.getParent(); inheritOnly = true; } return Selector.NEVER_APPLIES; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy