package de.neuland.jade4j.parser.node; import java.util.*; import de.neuland.jade4j.compiler.Utils; import de.neuland.jade4j.exceptions.ExpressionException; import de.neuland.jade4j.exceptions.JadeCompilerException; import de.neuland.jade4j.expression.ExpressionHandler; import de.neuland.jade4j.model.JadeModel; import de.neuland.jade4j.template.JadeTemplate; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; public abstract class AttrsNode extends Node { private static final String[] selfClosingTags = {"area", "base", "br", "col", "embed", "hr", "img", "input", "keygen", "link", "menuitem", "meta", "param", "source", "track", "wbr"}; protected LinkedList<Attr> attributes = new LinkedList<Attr>(); protected LinkedList<String> attributeBlocks = new LinkedList<String>(); protected List<String> attributeNames = new LinkedList<String>(); protected boolean selfClosing = false; protected Node codeNode; private boolean textOnly; public AttrsNode setAttribute(String key, Object value, boolean escaped) { if (!"class".equals(key) && this.attributeNames.indexOf(key) != -1) { throw new Error("Duplicate attribute '" + key + "' is not allowed."); } this.attributeNames.add(key); Attr attr = new Attr(key,value,escaped); this.attributes.add(attr); return this; } public String getAttribute(String key) { for (int i = 0, len = this.attributes.size(); i < len; ++i) { if (this.attributes.get(i) != null && this.attributes.get(i).getName().equals(key)) { return attributeValueToString(this.attributes.get(i).getValue()); } } return null; } private String attributeValueToString(Object value) { if (value instanceof ExpressionString) { String expression = ((ExpressionString) value).getValue(); return "#{" + expression + "}"; } return value.toString(); } // protected Map<String, Object> mergeInheritedAttributes(JadeModel model) { // List<Attr> mergedAttributes = this.attributes; // // if (inheritsAttributes) { // Object o = model.get("attributes"); // if (o != null && o instanceof Map) { // @SuppressWarnings("unchecked") // Map<String, Object> inheritedAttributes = (Map<String, Object>) o; // // for (Entry<String, Object> entry : inheritedAttributes.entrySet()) { // setAttribute(mergedAttributes, (String) entry.getKey(), entry.getValue()); // } // } // } // return mergedAttributes; // } @Override public AttrsNode clone() throws CloneNotSupportedException { AttrsNode clone = (AttrsNode) super.clone(); // shallow copy if (this.attributes != null) { clone.attributes = new LinkedList<Attr>(this.attributes); } if (this.attributes != null) { clone.attributeBlocks = new LinkedList<String>(this.attributeBlocks); } return clone; } public void addAttributes(String src){ this.attributeBlocks.add(src); } public void setSelfClosing(boolean selfClosing) { this.selfClosing = selfClosing; } public boolean isSelfClosing() { return selfClosing; } public void setTextOnly(boolean textOnly) { this.textOnly = textOnly; } public boolean isTextOnly() { return this.textOnly; } public void setCodeNode(Node codeNode) { this.codeNode = codeNode; } public Node getCodeNode() { return codeNode; } public boolean hasCodeNode() { return codeNode != null; } protected String visitAttributes(JadeModel model, JadeTemplate template) { LinkedList<Attr> newAttributes = new LinkedList<Attr>(attributes); if(attributeBlocks.size()>0){ //Todo: AttributesBlock needs to be evaluated for (String attributeBlock : attributeBlocks) { Object o = null; try { o = template.getExpressionHandler().evaluateExpression(attributeBlock, model); } catch (ExpressionException e) { e.printStackTrace(); } if(o instanceof Map) { Map<String, String> map = (Map<String, String>) o; for (Map.Entry<String, String> entry : map.entrySet()) { Attr attr = new Attr(String.valueOf(entry.getKey()),entry.getValue(),false); newAttributes.add(attr); } } if(o instanceof ArrayList){ ArrayList<Object> list = (ArrayList<Object>) o; for (Object o1 : list) { } } } LinkedHashMap<String,String> attrs = attrs(model, template,newAttributes); return attrsToString(attrs, template); }else{ LinkedHashMap<String,String> attrs = attrs(model, template, newAttributes); return attrsToString(attrs, template); } } private String attrsToString(LinkedHashMap<String, String> attrs, JadeTemplate template) { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : attrs.entrySet()) { sb.append(" "); sb.append(entry.getKey()); if(entry.getValue() != null) { sb.append("=").append('"'); sb.append(entry.getValue()); sb.append('"'); } } return sb.toString(); } protected LinkedHashMap<String,String> attrs(JadeModel model, JadeTemplate template, LinkedList<Attr> attrs) { ArrayList<String> classes = new ArrayList<String>(); ArrayList<Boolean> classEscaping = new ArrayList<Boolean>(); LinkedHashMap<String,String> newAttributes = new LinkedHashMap<String,String>(); for (Attr attribute : attrs) { try { addAttributesToMap(newAttributes,classes,classEscaping, attribute, model, template); } catch (ExpressionException e) { throw new JadeCompilerException(this, template.getTemplateLoader(), e); } } LinkedHashMap<String,String> finalAttributes = new LinkedHashMap<String,String>(); finalAttributes.putAll(newAttributes); if(!classes.isEmpty()){ finalAttributes.put("class", StringUtils.join(classes," ")); } return finalAttributes; } private void addAttributesToMap(HashMap<String, String> newAttributes, ArrayList<String> classes, ArrayList<Boolean> classEscaping, Attr attribute, JadeModel model, JadeTemplate template) throws ExpressionException { String name = attribute.getName(); boolean escaped = attribute.isEscaped(); // if ("class".equals(key)) { // classes.push(attr.val); // classEscaping.push(attr.escaped); // } else if (isConstant(attr.val)) { // if (buffer) { // this.buffer(runtime.attr(key, toConstant(attr.val), escaped, this.terse)); // } else { // var val = toConstant(attr.val); // if (key === 'style') val = runtime.style(val); // if (escaped && !(key.indexOf('data') === 0 && typeof val !== 'string')) { // val = runtime.escape(val); // } // buf.push(utils.stringify(key) + ': ' + utils.stringify(val)); // } // } else { // if (buffer) { // this.bufferExpression('jade.attr("' + key + '", ' + attr.val + ', ' + utils.stringify(escaped) + ', ' + utils.stringify(this.terse) + ')'); // } else { // var val = attr.val; // if (key === 'style') { // val = 'jade.style(' + val + ')'; // } // if (escaped && !(key.indexOf('data') === 0)) { // val = 'jade.escape(' + val + ')'; // } else if (escaped) { // val = '(typeof (jade_interp = ' + val + ') == "string" ? jade.escape(jade_interp) : jade_interp)'; // } // buf.push(utils.stringify(key) + ': ' + val); // } // } String value = null; Object attributeValue = attribute.getValue(); if("class".equals(name)) { if (attributeValue instanceof String) { escaped = attribute.isEscaped(); value = getInterpolatedAttributeValue(name, attributeValue,escaped, model, template); } else if (attributeValue instanceof ExpressionString) { escaped = ((ExpressionString) attributeValue).isEscape(); Object expressionValue = evaluateExpression((ExpressionString) attributeValue, model,template.getExpressionHandler()); //Array to String if (expressionValue != null && expressionValue.getClass().isArray()) { StringBuffer s = new StringBuffer(""); boolean first = true; if (expressionValue instanceof int[]) { for (int o : (int[]) expressionValue) { if (!first) s.append(" "); s.append(o); first = false; } } else { for (Object o : (Object[]) expressionValue) { if (!first) s.append(" "); s.append(o.toString()); first = false; } } value = s.toString(); }else if (expressionValue != null && expressionValue instanceof Map) { Map<String,Object> map = (Map<String,Object>) expressionValue; for (Map.Entry<String,Object> entry : map.entrySet()) { if(entry.getValue() instanceof Boolean){ if(((Boolean) entry.getValue()) == true){ classes.add(entry.getKey()); classEscaping.add(false); } } } }else if(expressionValue!=null && expressionValue instanceof Boolean){ if((Boolean) expressionValue) value = expressionValue.toString(); }else if(expressionValue!=null){ value = expressionValue.toString(); } } if(!StringUtils.isBlank(value)) { classes.add(value); classEscaping.add(escaped); } return; } else if (attributeValue instanceof ExpressionString) { // isConstant ExpressionString expressionString = (ExpressionString) attributeValue; escaped = expressionString.isEscape(); Object expressionValue = evaluateExpression(expressionString, model, template.getExpressionHandler()); if (expressionValue == null) { return; } // TODO: refactor if (expressionValue instanceof Boolean) { Boolean booleanValue = (Boolean) expressionValue; if (booleanValue) { value = name; } else { return; } if (template.isTerse()) { value = null; } }else{ value = expressionValue.toString(); if(escaped) value = StringEscapeUtils.escapeHtml4(value); } }else if (attributeValue instanceof String) { escaped = attribute.isEscaped(); value = getInterpolatedAttributeValue(name, attributeValue, escaped, model, template); } else if (attributeValue instanceof Boolean) { Boolean booleanValue = (Boolean) attributeValue; if (booleanValue) { value = name; } else { return; } if (template.isTerse()) { value = null; } } newAttributes.put(name,value); } private Object evaluateExpression(ExpressionString attribute, JadeModel model, ExpressionHandler expressionHandler) throws ExpressionException { String expression = ((ExpressionString) attribute).getValue(); Object result = expressionHandler.evaluateExpression(expression, model); if (result instanceof ExpressionString) { return evaluateExpression((ExpressionString) result, model, expressionHandler); } return result; } private String getInterpolatedAttributeValue(String name, Object attribute, boolean escaped, JadeModel model, JadeTemplate template) throws JadeCompilerException { // if (!preparedAttributeValues.containsKey(name)) { // preparedAttributeValues.put(name, Utils.prepareInterpolate((String) attribute, escaped)); // } List<Object> prepared = Utils.prepareInterpolate((String) attribute, escaped); try { return Utils.interpolate(prepared, model,template.getExpressionHandler()); } catch (ExpressionException e) { throw new JadeCompilerException(this, template.getTemplateLoader(), e); } } public boolean isTerse(JadeTemplate template) { return isSelfClosing(template) && template.isTerse(); } public boolean isSelfClosing(JadeTemplate template) { return !template.isXml() && ArrayUtils.contains(selfClosingTags, name); } }