/* * Copyright 2012 astamuse company,Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.astamuse.asta4d.render; import static com.astamuse.asta4d.render.SpecialRenderer.Clear; import org.apache.commons.lang3.tuple.Pair; import org.jsoup.nodes.Element; import com.astamuse.asta4d.Context; import com.astamuse.asta4d.extnode.ExtNodeConstants; import com.astamuse.asta4d.render.test.TestableRendering; import com.astamuse.asta4d.util.IdGenerator; /** * An AttributeSetter is used to set attribute value of an element. The specified attribute name can be a plain text representing the target * attribute, and it can also be prefixed by an additional character: + or -. * <p> * There are several cases about how AttributeSetter perform its action by different attribute name and value.<br> * * <li>new AttriuteSetter("+class", value): call addClass(value) on target Element, null value will be treated as "null". * * <li>new AttriuteSetter("-class", value): call removeClass(value) on target Element, null value will be treated as "null". * * <li>new AttriuteSetter("class", value): call attr("class", value) on target Element if value is not null, for a null value, * removeAttr("class") will be called. * * <li>new AttriuteSetter("anyattr", value): call attr("anyattr", value) on target Element if value is not null, for a null value, * removeAttr("anyattr") will be called. * * <li>new AttriuteSetter("anyattr", {@link SpecialRenderer#Clear}): call removeAttr("anyattr") on target Element. * * <li>new AttriuteSetter("+anyattr", value): call attr("anyattr", value) on target Element if value is not null, for a null value, * removeAttr("anyattr") will be called. * * <li>new AttriuteSetter("+anyattr", {@link SpecialRenderer#Clear}): call removeAttr("anyattr") on target Element. * * <li>new AttriuteSetter("-anyattr", value): call removeAttr("anyattr") on target Element. * * * * @author e-ryu * */ public class AttributeSetter implements ElementSetter, TestableRendering { private static enum ActionType { SET { @Override protected void configure(Element elem, String attrName, Object attrValue) { if (attrValue instanceof String) { elem.removeAttr(ExtNodeConstants.ATTR_DATAREF_PREFIX_WITH_NS + attrName); elem.attr(attrName, attrValue.toString()); } else if (attrValue instanceof Number) { elem.removeAttr(ExtNodeConstants.ATTR_DATAREF_PREFIX_WITH_NS + attrName); elem.attr(attrName, attrValue.toString()); } else if (attrValue instanceof Boolean) { elem.removeAttr(ExtNodeConstants.ATTR_DATAREF_PREFIX_WITH_NS + attrName); elem.attr(attrName, attrValue.toString()); } else { String dataRefId = attrName + "_" + IdGenerator.createId(); Context context = Context.getCurrentThreadContext(); context.setData(Context.SCOPE_EXT_ATTR, dataRefId, attrValue); elem.removeAttr(attrName); elem.attr(ExtNodeConstants.ATTR_DATAREF_PREFIX_WITH_NS + attrName, dataRefId); } } }, REMOVE { @Override protected void configure(Element elem, String attrName, Object attrValue) { elem.removeAttr(ExtNodeConstants.ATTR_DATAREF_PREFIX_WITH_NS + attrName); elem.removeAttr(attrName); // for remove all class definition, we have to clear the // classNames too. if (attrName.equals("class")) { elem.classNames().clear(); } } }, ADDCLASS { @Override protected void configure(Element elem, String attrName, Object attrValue) { elem.addClass(attrValue.toString()); } }, REMOVECLASS { @Override protected void configure(Element elem, String attrName, Object attrValue) { elem.removeClass(attrValue.toString()); } }; protected abstract void configure(Element elem, String attrName, Object attrValue); } /** * this value is for test purpose */ private String originalAttrName; /** * this value is for test purpose */ private Object originalValue; private String attrName; private Object attrValue; private ActionType actionType; /** * Constructor * * @param attr * attribute name * @param value * attribute value */ public AttributeSetter(String attr, Object value) { super(); this.originalAttrName = attr; this.originalValue = value; if (attr.equalsIgnoreCase("+class")) { this.actionType = ActionType.ADDCLASS; this.attrName = "class"; } else if (attr.equalsIgnoreCase("-class")) { this.actionType = ActionType.REMOVECLASS; this.attrName = "class"; } else if (attr.equalsIgnoreCase("class")) { if (value == null) { this.actionType = ActionType.REMOVE; this.attrName = "class"; } else { this.actionType = ActionType.SET; this.attrName = "class"; } } else { if (attr.startsWith("-")) { this.actionType = ActionType.REMOVE; this.attrName = attr.substring(1); } else { if (attr.startsWith("+")) { this.attrName = attr.substring(1); } else { this.attrName = attr; } if (value == null) { this.actionType = ActionType.REMOVE; } else if (value == Clear) { this.actionType = ActionType.REMOVE; } else { this.actionType = ActionType.SET; } } } this.attrValue = value == null ? "null" : value; if (actionType == ActionType.ADDCLASS || actionType == ActionType.REMOVECLASS) { if (!(attrValue instanceof String)) { throw new IllegalArgumentException("Only String type is allowed in class setting but found unexpected value type : " + attrValue.getClass().getName()); } } } @Override public void set(Element elem) { actionType.configure(elem, attrName, attrValue); } @Override public String toString() { return actionType + " attribute " + attrName + " for value [" + attrValue + "]"; } @Override public Object retrieveTestableData() { return Pair.of(originalAttrName, originalValue); } }