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

com.webfirmframework.wffweb.tag.html.attribute.core.AbstractAttribute Maven / Gradle / Ivy

There is a newer version: 12.0.0
Show newest version
package com.webfirmframework.wffweb.tag.html.attribute.core;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.WeakHashMap;
import java.util.logging.Logger;

import com.webfirmframework.wffweb.tag.core.AbstractTagBase;
import com.webfirmframework.wffweb.tag.html.AbstractHtml;
import com.webfirmframework.wffweb.tag.html.attribute.listener.AttributeValueChangeListener;
import com.webfirmframework.wffweb.tag.html.model.AbstractHtml5SharedObject;
import com.webfirmframework.wffweb.util.StringBuilderUtil;
import com.webfirmframework.wffweb.util.WffBinaryMessageUtil;

public abstract class AbstractAttribute extends AbstractTagBase {

    private static final long serialVersionUID = 1_1_0L;

    public static final Logger LOGGER = Logger
            .getLogger(AbstractAttribute.class.getName());

    private static final Security ACCESS_OBJECT;

    private String attributeName;
    private String attributeValue;
    private Map attributeValueMap;
    private Set attributeValueSet;

    private transient Set ownerTags;

    private StringBuilder tagBuilder;

    // private AttributeValueChangeListener valueChangeListener;

    private Set valueChangeListeners;

    private transient Charset charset = Charset.defaultCharset();

    // for security purpose, the class name should not be modified
    private static final class Security implements Serializable {

        private static final long serialVersionUID = 1L;

        private Security() {
        }
    }

    static {
        ACCESS_OBJECT = new Security();
    }

    {
        init();
    }

    private void init() {
        tagBuilder = new StringBuilder();
        ownerTags = Collections
                .newSetFromMap(new WeakHashMap());
        setRebuild(true);
    }

    /**
     * @return {@code String} equalent to the html string of the tag including
     *         the child tags.
     * @since 1.0.0
     * @author WFF
     */
    protected String getPrintStructure() {
        String printStructure = null;
        if (isRebuild() || isModified()) {
            printStructure = getPrintStructure(isRebuild());
            setRebuild(false);
        } else {
            printStructure = tagBuilder.toString();
        }
        return printStructure;
    }

    protected String getPrintStructure(final boolean rebuild) {
        String result = "";
        if (rebuild || isRebuild() || isModified()) {
            beforePrintStructure();
            tagBuilder.delete(0, tagBuilder.length());
            // tagBuildzer.append(" ");
            tagBuilder.append(attributeName);
            if (attributeValue != null) {
                tagBuilder.append(new char[] { '=', '"' });
                tagBuilder.append(attributeValue);
                result = StringBuilderUtil.getTrimmedString(tagBuilder) + '"';
                tagBuilder.append('"');
            } else if (attributeValueMap != null
                    && attributeValueMap.size() > 0) {
                tagBuilder.append(new char[] { '=', '"' });
                final Set> entrySet = getAttributeValueMap()
                        .entrySet();
                for (final Entry entry : entrySet) {
                    tagBuilder.append(entry.getKey());
                    tagBuilder.append(':');
                    tagBuilder.append(entry.getValue());
                    tagBuilder.append(';');
                }

                result = StringBuilderUtil.getTrimmedString(tagBuilder) + '"';
                tagBuilder.append('"');
            } else if (attributeValueSet != null
                    && attributeValueSet.size() > 0) {
                tagBuilder.append(new char[] { '=', '"' });
                for (final String each : getAttributeValueSet()) {
                    tagBuilder.append(each);
                    tagBuilder.append(' ');
                }
                result = StringBuilderUtil.getTrimmedString(tagBuilder) + '"';
                tagBuilder.append('"');
            } else {
                result = tagBuilder.toString();
            }
            /*
             * int lastIndex = tagBuilder.length() - 1;
             *
             * should be analyzed for optimization (as the deleteCharAt method
             * might compromise performance).
             *
             * if (Character.isWhitespace(tagBuilder.charAt(lastIndex))) {
             * tagBuilder.deleteCharAt(lastIndex); }
             */
            // result = StringBuilderUtil.getTrimmedString(tagBuilder) + "\"";
            // tagBuilder.append("\"");
            setRebuild(false);
        }
        tagBuilder.trimToSize();
        return result;
    }

    /**
     * gets the attribute name and value in the format of name=value. 
* Eg: style=color:green;background:blue
* This reduces 2 bytes taken for ". * * @return the attribute name and value in the format of name=value. Eg: * style=color:green;background:blue * @since 2.0.0 * @author WFF */ protected String getWffPrintStructure() { String result = ""; beforeWffPrintStructure(); final StringBuilder attrBuilder = new StringBuilder(); attrBuilder.append(attributeName); if (attributeValue != null) { attrBuilder.append(new char[] { '=' }); attrBuilder.append(attributeValue); result = StringBuilderUtil.getTrimmedString(attrBuilder); } else if (attributeValueMap != null && attributeValueMap.size() > 0) { attrBuilder.append(new char[] { '=' }); final Set> entrySet = getAttributeValueMap() .entrySet(); for (final Entry entry : entrySet) { attrBuilder.append(entry.getKey()); attrBuilder.append(':'); attrBuilder.append(entry.getValue()); attrBuilder.append(';'); } result = StringBuilderUtil.getTrimmedString(attrBuilder); } else if (attributeValueSet != null && attributeValueSet.size() > 0) { attrBuilder.append(new char[] { '=' }); for (final String each : getAttributeValueSet()) { attrBuilder.append(each); attrBuilder.append(' '); } result = StringBuilderUtil.getTrimmedString(attrBuilder); } else { result = attrBuilder.toString(); } return result; } /** * gets the attribute name and value in the format of name=value.
* Eg: style=color:green;background:blue
* This reduces 2 bytes taken for ". * * @return the attribute name and value in the format of name=value. Eg: * style=color:green;background:blue; * @since 2.0.0 * @author WFF */ public String toWffString() { return getWffPrintStructure(); } protected void beforeWffPrintStructure() { // NOT override and use if required } /** * gives compressed by index bytes for the attribute and value. The first * byte represents the attribute name index bytes length, the next bytes * represent the attribute name index bytes and the remaining bytes * represent attribute value without = and ". * * @param rebuild * @return the compressed by index bytes. * @throws IOException * @since 1.1.3 * @author WFF */ protected byte[] getBinaryStructureCompressedByIndex(final boolean rebuild) throws IOException { final String charset = this.charset.name(); // TODO review code final ByteArrayOutputStream compressedByIndexBytes = new ByteArrayOutputStream(); byte[] compressedBytes = new byte[0]; // String result = ""; if (rebuild || isRebuild() || isModified()) { beforePrintStructureCompressedByIndex(); // tagBuildzer.append(" "); final int attributeNameIndex = AttributeRegistry.getAttributeNames() .indexOf(attributeName); if (attributeNameIndex == -1) { compressedByIndexBytes.write(new byte[] { (byte) 0 }); compressedByIndexBytes .write(attributeName.concat("=").getBytes(charset)); LOGGER.warning(attributeName + " is not indexed, please register it with AttributeRegistrar"); } else { final byte[] optimizedBytesFromInt = WffBinaryMessageUtil .getOptimizedBytesFromInt(attributeNameIndex); compressedByIndexBytes.write( new byte[] { (byte) optimizedBytesFromInt.length }); compressedByIndexBytes.write(optimizedBytesFromInt); } if (attributeValue != null) { compressedByIndexBytes.write(attributeValue.getBytes(charset)); compressedBytes = compressedByIndexBytes.toByteArray(); } else if (attributeValueMap != null && attributeValueMap.size() > 0) { final Set> entrySet = getAttributeValueMap() .entrySet(); for (final Entry entry : entrySet) { compressedByIndexBytes .write(entry.getKey().getBytes(charset)); compressedByIndexBytes.write(new byte[] { ':' }); compressedByIndexBytes .write(entry.getValue().getBytes(charset)); compressedByIndexBytes.write(new byte[] { ';' }); } compressedBytes = compressedByIndexBytes.toByteArray(); } else if (attributeValueSet != null && attributeValueSet.size() > 0) { for (final String each : getAttributeValueSet()) { compressedByIndexBytes.write(each.getBytes(charset)); compressedByIndexBytes.write(new byte[] { ' ' }); } compressedBytes = compressedByIndexBytes.toByteArray(); } else { compressedBytes = compressedByIndexBytes.toByteArray(); } setRebuild(false); } return compressedBytes; } /** * @return the attributeName set by * {@code AbstractAttribute#setAttributeName(String)} * @since 1.0.0 * @author WFF */ public String getAttributeName() { return attributeName; } /** * Set attribute name, eg: width, height, name, type etc.. * * @param attributeName * the attributeName to set * @since 1.0.0 * @author WFF */ protected void setAttributeName(final String attributeName) { this.attributeName = attributeName; } /* * (non-Javadoc) * * @see com.webfirmframework.wffweb.tag.Base#toHtmlString() * * @since 1.0.0 * * @author WFF */ @Override public String toHtmlString() { return getPrintStructure(true); } public byte[] toCompressedBytesByIndex(final boolean rebuild) throws IOException { return getBinaryStructureCompressedByIndex(rebuild); } public byte[] toCompressedBytesByIndex(final boolean rebuild, final Charset charset) throws IOException { final Charset previousCharset = this.charset; try { this.charset = charset; return toCompressedBytesByIndex(rebuild); } finally { this.charset = previousCharset; } } /* * (non-Javadoc) * * @see com.webfirmframework.wffweb.tag.core.TagBase#toHtmlString(java.nio. * charset.Charset) */ @Override public String toHtmlString(final Charset charset) { final Charset previousCharset = this.charset; try { this.charset = charset; return toHtmlString(); } finally { this.charset = previousCharset; } } /* * (non-Javadoc) * * @see com.webfirmframework.wffweb.tag.core.TagBase#toHtmlString(java.lang. * String) */ @Override public String toHtmlString(final String charset) { final Charset previousCharset = this.charset; try { this.charset = Charset.forName(charset); return toHtmlString(); } finally { this.charset = previousCharset; } } /* * (non-Javadoc) * * @see com.webfirmframework.wffweb.tag.Base#toHtmlString(boolean) * * @since 1.0.0 * * @author WFF */ @Override public String toHtmlString(final boolean rebuild) { return getPrintStructure(rebuild); } /* * (non-Javadoc) * * @see com.webfirmframework.wffweb.tag.core.TagBase#toHtmlString(boolean, * java.nio.charset.Charset) */ @Override public String toHtmlString(final boolean rebuild, final Charset charset) { final Charset previousCharset = this.charset; try { this.charset = charset; return toHtmlString(rebuild); } finally { this.charset = previousCharset; } } /* * (non-Javadoc) * * @see com.webfirmframework.wffweb.tag.core.TagBase#toHtmlString(boolean, * java.lang.String) */ @Override public String toHtmlString(final boolean rebuild, final String charset) { final Charset previousCharset = this.charset; try { this.charset = Charset.forName(charset); return toHtmlString(rebuild); } finally { this.charset = previousCharset; } } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return getPrintStructure(); } /** * @return the attributeValueMap * @since 1.0.0 * @author WFF */ protected Map getAttributeValueMap() { if (attributeValueMap == null) { setAttributeValueMap(new LinkedHashMap()); } return attributeValueMap; } /** * @param attributeValueMap * the attributeValueMap to set * @since 1.0.0 * @author WFF */ protected void setAttributeValueMap( final Map attributeValueMap) { if (!Objects.equals(attributeValueMap, this.attributeValueMap)) { setModified(true); } this.attributeValueMap = attributeValueMap; } /** * adds the given key value. * * @param key * @param value * @return true if it is modified * @since 1.0.0 * @author WFF */ protected boolean addToAttributeValueMap(final String key, final String value) { final Map attributeValueMap = getAttributeValueMap(); final String previousValue = attributeValueMap.put(key, value); if (!Objects.equals(previousValue, value)) { setModified(true); invokeValueChangeListeners(); return true; } // if (!attributeValueMap.containsKey(key) // || !value.equals(attributeValueMap.get(key))) { // // // setModified(true); // return true; // } return false; } private void invokeValueChangeListeners() { final Set sharedObjects = new HashSet( ownerTags.size()); for (final AbstractHtml ownerTag : ownerTags) { sharedObjects.add(ownerTag.getSharedObject()); } for (final AbstractHtml5SharedObject sharedObject : sharedObjects) { final AttributeValueChangeListener valueChangeListener = sharedObject .getValueChangeListener(ACCESS_OBJECT); if (valueChangeListener != null) { // ownerTags should not be modified in the consuming // part, here // skipped it making unmodifiable to gain // performance final AttributeValueChangeListener.Event event = new AttributeValueChangeListener.Event( this, ownerTags); valueChangeListener.valueChanged(event); } } if (valueChangeListeners != null) { for (final AttributeValueChangeListener listener : valueChangeListeners) { final AttributeValueChangeListener.Event event = new AttributeValueChangeListener.Event( AbstractAttribute.this, Collections.unmodifiableSet(ownerTags)); listener.valueChanged(event); } } } /** * adds all to the attribute value map. * * @param map * @since 1.0.0 * @author WFF * @return true if it is modified */ protected boolean addAllToAttributeValueMap(final Map map) { if (map != null && map.size() > 0) { getAttributeValueMap().putAll(map); setModified(true); invokeValueChangeListeners(); return true; } return false; } /** * removes the key value for the input key. * * @param key * @since 1.0.0 * @author WFF * @return true if the given key (as well as value contained corresponding * to it) has been removed. */ protected boolean removeFromAttributeValueMap(final String key) { boolean result = false; if (getAttributeValueMap().containsKey(key)) { setModified(true); result = true; getAttributeValueMap().remove(key); } return result; } /** * removes only if the key and value matches in the map for any particular * entry. * * @param key * @param value * @since 1.0.0 * @author WFF * @return true if it is modified */ protected boolean removeFromAttributeValueMap(final String key, final String value) { final String previousValue = getAttributeValueMap().remove(key); if (!Objects.equals(previousValue, value)) { setModified(true); invokeValueChangeListeners(); return true; } return false; } protected void removeAllFromAttributeValueMap() { if (attributeValueMap != null && getAttributeValueMap().size() > 0) { getAttributeValueMap().clear(); setModified(true); invokeValueChangeListeners(); } } /** * @return the attributeValue * @since 1.0.0 * @author WFF */ public String getAttributeValue() { return attributeValue; } /** * @param attributeValue * the attributeValue to set * @since 1.0.0 * @author WFF */ protected void setAttributeValue(final String attributeValue) { if (!Objects.equals(this.attributeValue, attributeValue)) { setModified(true); invokeValueChangeListeners(); } this.attributeValue = attributeValue; } /** * @returns one of the ownerTags * @since 1.0.0 * @author WFF * @deprecated this method may be removed later as there could be multiple * owner tags. */ @Deprecated public AbstractHtml getOwnerTag() { if (ownerTags.iterator().hasNext()) { return ownerTags.iterator().next(); } return null; } /** * * @return the tags which are consuming this attribute as an array * @since 2.0.0 * @author WFF */ public AbstractHtml[] getOwnerTags() { // returning the set is not good because // if the AbstractHtml needs to be // modified while iterating the set will cause // ConcurrentModificationException return ownerTags.toArray(new AbstractHtml[ownerTags.size()]); } /** * NB:- this method is used for internal purpose, so it should not be * consumed. * * @param ownerTag * the ownerTag to set * @since 1.0.0 * @author WFF */ public void setOwnerTag(final AbstractHtml ownerTag) { ownerTags.add(ownerTag); } /** * NB:- this method is used for internal purpose, so it should not be * consumed. * * @param ownerTag * the ownerTag to unset * @return true if the given ownerTag is an owner of the attribute. * @since 2.0.0 * @author WFF */ public boolean unsetOwnerTag(final AbstractHtml ownerTag) { return ownerTags.remove(ownerTag); } @Override public void setModified(final boolean modified) { super.setModified(modified); for (final AbstractHtml ownerTag : ownerTags) { if (ownerTag.getSharedObject() != null) { ownerTag.getSharedObject().setChildModified(modified); } } } /** * NB:- this is only for getting values. Use addToAttributeValueSet method * for adding * * @return the attributeValueSet * @since 1.0.0 * @author WFF */ protected Set getAttributeValueSet() { if (attributeValueSet == null) { attributeValueSet = new LinkedHashSet(); } return attributeValueSet; } /** * @param attributeValueSet * the attributeValueSet to set * @since 1.0.0 * @author WFF */ protected void setAttributeValueSet(final Set attributeValueSet) { if (!Objects.equals(this.attributeValueSet, attributeValueSet)) { setModified(true); } this.attributeValueSet = attributeValueSet; } /** * adds to the attribute value set. * * @param value * @since 1.0.0 * @author WFF * @return */ protected boolean addToAttributeValueSet(final String value) { final boolean added = getAttributeValueSet().add(value); if (added) { setModified(true); invokeValueChangeListeners(); } return added; } /** * adds all to the attribute value set. * * @param value * @since 1.0.0 * @author WFF */ protected void addAllToAttributeValueSet(final Collection values) { if (values != null) { final boolean added = getAttributeValueSet().addAll(values); if (added) { setModified(true); invokeValueChangeListeners(); } } } /** * removes the value from the the attribute set. * * @param value * @since 1.0.0 * @author WFF */ protected void removeFromAttributeValueSet(final String value) { final boolean removed = getAttributeValueSet().remove(value); if (removed) { setModified(true); invokeValueChangeListeners(); } } /** * removes the value from the the attribute set. * * @param values * @since 1.0.0 * @author WFF */ protected void removeAllFromAttributeValueSet( final Collection values) { final boolean removedAll = getAttributeValueSet().removeAll(values); if (removedAll) { setModified(true); invokeValueChangeListeners(); } } /** * clears all values from the value set. * * @since 1.0.0 * @author WFF */ protected void removeAllFromAttributeValueSet() { getAttributeValueSet().clear(); setModified(true); invokeValueChangeListeners(); } /** * invokes just before {@code getPrintStructure(final boolean} method and * only if the getPrintStructure(final boolean} rebuilds the structure. * * @since 1.0.0 * @author WFF */ protected void beforePrintStructure() { // TODO override and use } /** * invokes just before * {@code getPrintStructureCompressedByIndex(final boolean} method and only * if the getPrintStructureCompressedByIndex(final boolean} rebuilds the * structure. * * @since 1.0.0 * @author WFF */ protected void beforePrintStructureCompressedByIndex() { // TODO override and use } /** * @return the charset */ public Charset getCharset() { return charset; } /** * @param charset * the charset to set */ public void setCharset(final Charset charset) { this.charset = charset; } /** * adds value change lister which will be invoked when the value changed * * @param valueChangeListener * @since 2.0.0 * @author WFF */ public void addValueChangeListener( final AttributeValueChangeListener valueChangeListener) { if (valueChangeListeners == null) { valueChangeListeners = new TreeSet(); } valueChangeListeners.add(valueChangeListener); } /** * removes the corresponding value change listener * * @param valueChangeListener * @since 2.0.0 * @author WFF */ public void removeValueChangeListener( final AttributeValueChangeListener valueChangeListener) { if (valueChangeListeners != null) { valueChangeListeners.remove(valueChangeListener); } } /** * @return the set of value change listeners * @since 2.0.0 * @author WFF */ public Set getValueChangeListeners() { return Collections.unmodifiableSet(valueChangeListeners); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy