/*
 * Copyright 2014-2019 Web Firm Framework
 *
 * 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.
 * @author WFF
 */
package com.webfirmframework.wffweb.css;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.webfirmframework.wffweb.InvalidValueException;
import com.webfirmframework.wffweb.NullValueException;
import com.webfirmframework.wffweb.clone.CloneUtil;
import com.webfirmframework.wffweb.css.core.AbstractCssProperty;
import com.webfirmframework.wffweb.data.Bean;
import com.webfirmframework.wffweb.informer.StateChangeInformer;
import com.webfirmframework.wffweb.util.StringUtil;

/**
 * cursor : alias | all-scroll | auto | cell | context-menu | col-resize | copy
 * | crosshair | default | e-resize | ew-resize | grab | grabbing | help | move
 * | n-resize | ne-resize | nesw-resize | ns-resize | nw-resize | nwse-resize |
 * no-drop | none | not-allowed | pointer | progress | row-resize | s-resize |
 * se-resize | sw-resize | text | URL | vertical-text | w-resize | wait |
 * zoom-in | zoom-out | initial | inherit;
 *
 * @author WFF
 * @since 1.0.0
 */
public class Cursor extends AbstractCssProperty<Cursor>
        implements StateChangeInformer<Bean> {

    private static final long serialVersionUID = 1_0_0L;

    public static final String ALIAS = "alias";
    public static final String ALL_SCROLL = "all-scroll";
    public static final String AUTO = "auto";
    public static final String CELL = "cell";
    public static final String CONTEXT_MENU = "context-menu";
    public static final String COL_RESIZE = "col-resize";
    public static final String COPY = "copy";
    public static final String CROSSHAIR = "crosshair";
    public static final String DEFAULT = "default";
    public static final String E_RESIZE = "e-resize";
    public static final String EW_RESIZE = "ew-resize";
    public static final String GRAB = "grab";
    public static final String GRABBING = "grabbing";
    public static final String HELP = "help";
    public static final String MOVE = "move";
    public static final String N_RESIZE = "n-resize";
    public static final String NE_RESIZE = "ne-resize";
    public static final String NESW_RESIZE = "nesw-resize";
    public static final String NS_RESIZE = "ns-resize";
    public static final String NW_RESIZE = "nw-resize";
    public static final String NWSE_RESIZE = "nwse-resize";
    public static final String NO_DROP = "no-drop";
    public static final String NONE = "none";
    public static final String NOT_ALLOWED = "not-allowed";
    public static final String POINTER = "pointer";
    public static final String PROGRESS = "progress";
    public static final String ROW_RESIZE = "row-resize";
    public static final String S_RESIZE = "s-resize";
    public static final String SE_RESIZE = "se-resize";
    public static final String SW_RESIZE = "sw-resize";
    public static final String TEXT = "text";
    public static final String VERTICAL_TEXT = "vertical-text";
    public static final String W_RESIZE = "w-resize";
    public static final String WAIT = "wait";
    public static final String ZOOM_IN = "zoom-in";
    public static final String ZOOM_OUT = "zoom-out";
    public static final String INITIAL = "initial";
    public static final String INHERIT = "inherit";

    private static final List<String> ALL_CURSORTYPES;

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

    static {
        ALL_CURSORTYPES = new ArrayList<>();
        ALL_CURSORTYPES.add(ALL_SCROLL);
        ALL_CURSORTYPES.add(AUTO);
        ALL_CURSORTYPES.add(CELL);
        ALL_CURSORTYPES.add(CONTEXT_MENU);
        ALL_CURSORTYPES.add(COL_RESIZE);
        ALL_CURSORTYPES.add(COPY);
        ALL_CURSORTYPES.add(CROSSHAIR);
        ALL_CURSORTYPES.add(DEFAULT);
        ALL_CURSORTYPES.add(E_RESIZE);
        ALL_CURSORTYPES.add(EW_RESIZE);
        ALL_CURSORTYPES.add(GRAB);
        ALL_CURSORTYPES.add(GRABBING);
        ALL_CURSORTYPES.add(HELP);
        ALL_CURSORTYPES.add(MOVE);
        ALL_CURSORTYPES.add(N_RESIZE);
        ALL_CURSORTYPES.add(NE_RESIZE);
        ALL_CURSORTYPES.add(NESW_RESIZE);
        ALL_CURSORTYPES.add(NS_RESIZE);
        ALL_CURSORTYPES.add(NW_RESIZE);
        ALL_CURSORTYPES.add(NWSE_RESIZE);
        ALL_CURSORTYPES.add(NO_DROP);
        ALL_CURSORTYPES.add(NONE);
        ALL_CURSORTYPES.add(NOT_ALLOWED);
        ALL_CURSORTYPES.add(POINTER);
        ALL_CURSORTYPES.add(PROGRESS);
        ALL_CURSORTYPES.add(ROW_RESIZE);
        ALL_CURSORTYPES.add(S_RESIZE);
        ALL_CURSORTYPES.add(SE_RESIZE);
        ALL_CURSORTYPES.add(SW_RESIZE);
        ALL_CURSORTYPES.add(TEXT);
        ALL_CURSORTYPES.add(VERTICAL_TEXT);
        ALL_CURSORTYPES.add(W_RESIZE);
        ALL_CURSORTYPES.add(WAIT);
        ALL_CURSORTYPES.add(ZOOM_IN);
        ALL_CURSORTYPES.add(ZOOM_OUT);
        ALL_CURSORTYPES.add(INITIAL);
        ALL_CURSORTYPES.add(INHERIT);
    }

    private String cssValue;

    private String[] cursorUrls;

    private String cursorType;

    private UrlCss3Value[] urlCss3Values;

    public Cursor() {
        setCssValue(DEFAULT);
    }

    /**
     * @param cssValue
     *                     the cssValue to set. eg:- {@code Cursor.ALIAS}
     * @author WFF
     */
    public Cursor(final String cssValue) {
        setCssValue(cssValue);
    }

    /**
     * @param cursor
     *                   the {@code Cursor} object from which the cssName and
     *                   cssValue to set.
     * @author WFF
     */
    public Cursor(final Cursor cursor) {
        if (cursor == null) {
            throw new NullValueException("customCss can not be null");
        }
        if (cursor.getCssName() == null) {
            throw new NullValueException(
                    "customCss.getCssName() can not be null");
        }
        if (StringUtil.endsWithColon(cursor.getCssName())) {
            throw new InvalidValueException(
                    "customCss.getCssName() can not end with : (colon)");
        }
        setCssValue(cursor.getCssValue());
    }

    /**
     * This constructor is for CSS 2.1 cursor syntax.

     * sample code :- {@code new Cursor("auto", "Test.gif", "TestImage.png")}
     * creates cursor: url("Test.gif"), url("TestImage.png"), auto;
     * . For css3 syntax method please use
     * {@code new Cursor(String cursorType, UrlCss3Value... urlCss3Values)} or
     * {@code setCursorUrls(String cursorType, UrlCss3Value... urlCss3Values)}
     * method.
     *
     * @param cursorType
     *                       This will be the last value, eg:- auto
     *                       in
     *                       cursor: url(Test.gif), url(TestImage.png), auto;.
     *                       This value can not be null. And, it will throw
     *                       {@code NullValueException} for null value.
     * @param cursorUrls
     *                       an array of cursor urls, eg:-
     *                       {@code cursor.setCursorUrls("auto", "Test.gif", "TestImage.png")}
     *                       and the generated css will be
     *                       cursor: url("Test.gif"), url("TestImage.png"), auto;
     * @since 1.0.0
     * @author WFF
     */
    public Cursor(final String cursorType, final String... cursorUrls) {
        setCursorUrls(cursorType, cursorUrls);
    }

    /**
     * @param cursorType
     *                          the any of the inbuilt cursor types. It will
     *                          come as the last one in the css value.
     * @param urlCss3Values
     *                          an array of {@code UrlCss3Value} objects.
     * @author WFF
     */
    public Cursor(final String cursorType,
            final UrlCss3Value... urlCss3Values) {
        setCursorUrls(cursorType, urlCss3Values);
    }

    /*
     * (non-Javadoc)
     *
     * @see com.webfirmframework.wffweb.css.CssProperty#getCssName()
     *
     * @since 1.0.0
     *
     * @author WFF
     */
    @Override
    public String getCssName() {
        return CssNameConstants.CURSOR;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.webfirmframework.wffweb.css.CssProperty#getCssValue()
     *
     * @since 1.0.0
     *
     * @author WFF
     */
    @Override
    public String getCssValue() {
        return cssValue;
    }

    @Override
    public String toString() {
        return getCssName() + ": " + getCssValue();
    }

    /**
     * @param cssValue
     *                     {@code null} is considered as an invalid value.
     * @since 1.0.0
     * @author WFF
     */
    @Override
    public Cursor setCssValue(final String cssValue) {
        if (cssValue == null) {
            throw new NullValueException("the value can not be null");
        }
        final String trimmedCssValue = StringUtil.strip(cssValue);
        final int cssValueLength = trimmedCssValue.length();
        if (cssValueLength > 0 && (trimmedCssValue.charAt(0) == ':'
                || trimmedCssValue.charAt(cssValueLength - 1) == ';')) {
            throw new InvalidValueException(
                    "cssValue can not start with : (colon) or end with ; (semicolon)");
        }

        final String[] cssValueParts = StringUtil.splitByComma(trimmedCssValue);
        final String cursorType = StringUtil
                .strip(cssValueParts[cssValueParts.length - 1]).toLowerCase();
        if (!ALL_CURSORTYPES.contains(StringUtil.strip(cursorType))) {
            throw new InvalidValueException(
                    "The last cursor should be the inbuild cursor like auto, default etc..");
        }
        if (cssValueParts.length > 1) {
            final StringBuilder cssValueBuilder = new StringBuilder();
            if (urlCss3Values == null) {
                urlCss3Values = new UrlCss3Value[cssValueParts.length - 1];
                for (int i = 0; i < cssValueParts.length - 1; i++) {
                    final UrlCss3Value urlCss3Value = new UrlCss3Value(
                            cssValueParts[i]);
                    urlCss3Value.setAlreadyInUse(true);
                    urlCss3Value.setStateChangeInformer(this);
                    urlCss3Values[i] = urlCss3Value;

                    cssValueBuilder.append(urlCss3Value.getValue())
                            .append(", ");
                }
            } else {

                UrlCss3Value[] urlCss3ValuesTemp = null;
                if ((cssValueParts.length - 1) == urlCss3Values.length) {
                    urlCss3ValuesTemp = urlCss3Values;
                } else {
                    urlCss3ValuesTemp = new UrlCss3Value[cssValueParts.length
                            - 1];
                }
                for (int i = 0; i < cssValueParts.length - 1; i++) {
                    final UrlCss3Value urlCss3Value;
                    if (i < urlCss3Values.length) {
                        urlCss3Value = urlCss3Values[i];
                        urlCss3Value.setUrlCssValue(cssValueParts[i]);
                    } else {
                        urlCss3Value = new UrlCss3Value(cssValueParts[i]);
                    }

                    urlCss3Value.setAlreadyInUse(true);
                    urlCss3Value.setStateChangeInformer(this);
                    urlCss3ValuesTemp[i] = urlCss3Value;
                    cssValueBuilder.append(urlCss3Value.getValue())
                            .append(", ");
                }

                for (int i = cssValueParts.length
                        - 1; i < urlCss3Values.length; i++) {
                    urlCss3Values[i].setAlreadyInUse(false);
                }

                for (final UrlCss3Value urlCss3Value : urlCss3ValuesTemp) {
                    urlCss3Value.setStateChangeInformer(this);
                    urlCss3Value.setAlreadyInUse(true);
                }
                urlCss3Values = urlCss3ValuesTemp;
            }
            cssValueBuilder.append(cursorType);
            this.cssValue = cssValueBuilder.toString();
            this.cursorType = cursorType;
        } else {
            this.cssValue = trimmedCssValue;
            this.cursorType = cursorType;
        }
        if (getStateChangeInformer() != null) {
            getStateChangeInformer().stateChanged(this);
        }
        return this;
    }

    /**
     * @param cursorType
     *                       it should be one of the following
     *                        Cursor.ALL_SCROLL ,
     *            Cursor.AUTO , Cursor.CELL , Cursor.CONTEXT_MENU ,
     *            Cursor.COL_RESIZE , Cursor.COPY , Cursor.CROSSHAIR ,
     *            Cursor.DEFAULT , Cursor.E_RESIZE , Cursor.EW_RESIZE ,
     *            Cursor.GRAB , Cursor.GRABBING , Cursor.HELP , Cursor.MOVE ,
     *            Cursor.N_RESIZE , Cursor.NE_RESIZE , Cursor.NESW_RESIZE ,
     *            Cursor.NS_RESIZE , Cursor.NW_RESIZE , Cursor.NWSE_RESIZE ,
     *            Cursor.NO_DROP , Cursor.NONE , Cursor.NOT_ALLOWED ,
     *            Cursor.POINTER , Cursor.PROGRESS , Cursor.ROW_RESIZE ,
     *            Cursor.S_RESIZE , Cursor.SE_RESIZE , Cursor.SW_RESIZE ,
     *            Cursor.TEXT , Cursor.URL , Cursor.VERTICAL_TEXT ,
     *            Cursor.W_RESIZE , Cursor.WAIT , Cursor.ZOOM_IN ,
     *            Cursor.ZOOM_OUT , Cursor.INITIAL or Cursor.INHERIT.
     * @since 1.0.0
     * @author WFF
     */
    public void setCursorType(final String cursorType) {
        this.cursorType = cursorType;
        final String cursor = StringUtil.strip(cursorType).toLowerCase();
        if (ALL_CURSORTYPES.contains(cursor)) {
            setCssValue(cursor);
        } else {
            throw new InvalidValueException(cursorType
                    + " is invalid. use Cursor.DEFAULT or Cursor.WAIT etc..");
        }
    }

    /**
     * @return
     * @since 1.0.0
     * @author WFF
     */
    public String getCursorType() {
        return cursorType;
    }

    /**
     * @return the cursor urls as an array set by
     *         {@code Cursor#setCursorUrls(String, String...)} method. The
     *         returned array is just a copy of the cursor urls, modifying it
     *         will not affect the {@code Cursor} object.
     * @since 1.0.0
     * @author WFF
     */
    public String[] getCursorUrls() {
        return cursorUrls;
    }

    /**
     * This method is for CSS 2.1 cursor syntax.

     * sample code :-
     * {@code cursor.setCursorUrls("auto", "Test.gif", "TestImage.png")} creates
     * cursor: url("Test.gif"), url("TestImage.png"), auto;. For
     * css3 syntax method please use
     * {@code setCursorUrls(String cursorType, UrlCss3Value... urlCss3Values)}
     * method.
     *
     * @param cursorType
     *                       This will be the last value, eg:- auto
     *                       in
     *                       cursor: url(Test.gif), url(TestImage.png), auto;.
     *                       This value can not be null. And, it will throw
     *                       {@code NullValueException} for null value.
     * @param cursorUrls
     *                       an array of cursor urls, eg:-
     *                       {@code cursor.setCursorUrls("auto", "Test.gif", "TestImage.png")}
     *                       and the generated css will be
     *                       cursor: url("Test.gif"), url("TestImage.png"), auto;
     * @since 1.0.0
     * @author WFF
     */
    public void setCursorUrls(final String cursorType,
            final String... cursorUrls) {
        if (cursorType == null) {
            throw new NullValueException("cursorType is null");
        }
        if (cursorUrls == null) {
            throw new NullValueException("cursorUrls is null");
        }
        if (!ALL_CURSORTYPES
                .contains(StringUtil.strip(cursorType).toLowerCase())) {
            throw new InvalidValueException(cursorType
                    + " is invalid. The cursorType should be any predefined cursor type, eg default, auto etc..");
        }
        final StringBuilder sb = new StringBuilder();
        for (final String cursorUrl : cursorUrls) {
            final String urlString = "url(\"".concat(cursorUrl).concat("\"), ");
            sb.append(urlString);
        }
        String cssValue = sb.toString();

        cssValue = cssValue.endsWith(", ") ? cssValue.concat(cursorType)
                : cursorType;

        setCssValue(cssValue);
        this.cursorType = cursorType;
        this.cursorUrls = cursorUrls;
    }

    /**
     * @param cursorType
     *                          * the any of the inbuilt cursor types. It will
     *                          come as the last one in the css value.
     * @param urlCss3Values
     *                          urlCss3Values an array of {@code UrlCss3Value}
     *                          objects.
     * @since 1.0.0
     * @author WFF
     */
    public void setCursorUrls(final String cursorType,
            final UrlCss3Value... urlCss3Values) {
        if (cursorType == null) {
            throw new NullValueException("cursorType is null");
        }
        if (urlCss3Values == null) {
            throw new NullValueException("urlCss3Values is null");
        }
        if (!ALL_CURSORTYPES
                .contains(StringUtil.strip(cursorType).toLowerCase())) {
            throw new InvalidValueException(cursorType
                    + " is invalid. The cursorType should be any predefined cursor type, eg default, auto etc..");
        }
        if (this.urlCss3Values != null) {
            for (final UrlCss3Value urlCss3Value : this.urlCss3Values) {
                urlCss3Value.setAlreadyInUse(false);
            }
        }

        final StringBuilder cssValueBuilder = new StringBuilder();
        for (UrlCss3Value urlCss3Value : urlCss3Values) {
            if (urlCss3Value.isAlreadyInUse()) {
                try {
                    final UrlCss3Value clonedUrlCss3Value = CloneUtil
                            .<UrlCss3Value> deepCloneOnlyIfDoesNotContain(
                                    urlCss3Value, this.urlCss3Values);

                    if (!Objects.equals(clonedUrlCss3Value, urlCss3Value)) {
                        urlCss3Value = clonedUrlCss3Value;
                        if (LOGGER.isLoggable(Level.WARNING)) {
                            LOGGER.warning("cloned urlCss3Value " + urlCss3Value
                                    + "(hashcode: " + urlCss3Value.hashCode()
                                    + ") as it is already used by another object");
                        }
                    }
                } catch (final CloneNotSupportedException e) {
                    if (LOGGER.isLoggable(Level.SEVERE)) {
                        LOGGER.log(Level.SEVERE, e.getMessage(), e);
                    }
                }
            }
            cssValueBuilder.append(urlCss3Value.getValue()).append(", ");
            urlCss3Value.setAlreadyInUse(true);
            urlCss3Value.setStateChangeInformer(this);
        }
        cssValueBuilder.append(cursorType);

        if (this.urlCss3Values != null) {
            for (final UrlCss3Value urlCss3Value : this.urlCss3Values) {
                urlCss3Value.setAlreadyInUse(false);
            }
        }

        for (final UrlCss3Value urlCss3Value : urlCss3Values) {
            urlCss3Value.setAlreadyInUse(true);
            urlCss3Value.setStateChangeInformer(this);
        }

        this.cursorType = cursorType;
        this.urlCss3Values = urlCss3Values;
        cssValue = cssValueBuilder.toString();
    }

    /**
     * @return a new object of unmodifiable {@code List<UrlCss3Value>} whenever
     *         this method is called. Or null.
     *
     * @author WFF
     * @since 1.0.0
     */
    public List<UrlCss3Value> getUrlCss3Values() {
        // for security improvements for urlCss3Values without
        // compromising performance.
        // TODO the drawback of this the returning of new object each time even
        // if the urlCss3Values array has not been modified, as a minor
        // performance
        // improvement, return a new object only if the urlCss3Values is
        // modified.
        if (urlCss3Values == null) {
            return null;
        }
        return Collections.unmodifiableList(Arrays.asList(urlCss3Values));
    }

    /**
     * @return all inbuilt cursorTypes eg:- auto, default, grab etc..
     * @since 1.0.0
     * @author WFF
     */
    public static String[] getAllCursorTypes() {
        return ALL_CURSORTYPES.toArray(new String[ALL_CURSORTYPES.size()]);
    }

    @Override
    public void stateChanged(final Bean stateChangedObject) {
        if (cursorType != null && urlCss3Values != null) {
            setCursorUrls(cursorType, urlCss3Values);
            if (getStateChangeInformer() != null) {
                getStateChangeInformer().stateChanged(this);
            }
        }
    }

    /**
     * To introduce additional cursor types, add those to this list.
     *
     * @return the allCursortypes
     * @author WFF
     * @since 1.0.0
     */
    protected static List<String> getAllCursortypes() {
        return ALL_CURSORTYPES;
    }
}


/*
 * Copyright 2014-2019 Web Firm Framework
 *
 * 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.
 * @author WFF
 */
package com.webfirmframework.wffweb.css.css3;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;

import com.webfirmframework.wffweb.InvalidValueException;
import com.webfirmframework.wffweb.NullValueException;
import com.webfirmframework.wffweb.css.CssLengthUnit;
import com.webfirmframework.wffweb.css.CssNameConstants;
import com.webfirmframework.wffweb.css.core.AbstractCssProperty;
import com.webfirmframework.wffweb.css.core.CssProperty;
import com.webfirmframework.wffweb.informer.StateChangeInformer;
import com.webfirmframework.wffweb.util.CssLengthUtil;
import com.webfirmframework.wffweb.util.ObjectUtil;
import com.webfirmframework.wffweb.util.StringUtil;
import com.webfirmframework.wffweb.util.TagStringUtil;

/**
 *

 *
 * border-image-slice: number|%|fill|initial|inherit;
 *
 * Examples:
 *
 *     border-image-slice:5% 10% 15% 10%;
 *         top border is 5%
 *         right border is 10%
 *         bottom border is 15%
 *         border is 10%
 *
 *     border-image-slice:5% 10% 15%;
 *         top border is 5%
 *         right and left borders are 10%
 *         bottom border is 15%
 *
 *     border-image-slice:5% 10%;
 *         top and bottom borders are 5%
 *         right and left borders are 10%
 *
 *     border-image-slice:5%;
 *         all four borders are 5%
 *
 * The border-image-slice property specifies how to slice the image specified by border-image-source. The image is always sliced into nine sections: four corners, four edges and the middle.
 *
 * The "middle" part is treated as fully transparent, unless the fill keyword is set.
 *
 * Tip: Also look at the border-image property (a shorthand property for setting all the border-image-* properties).
 * Default value:  100%
 * Inherited:      no
 * Animatable:     no
 * Version:        CSS3
 * JavaScript syntax:      object.style.borderImageSlice="30%"
 *

 *
 *
 * @author WFF
 * @since 1.0.0
 */
public class BorderImageSlice extends AbstractCssProperty<BorderImageSlice> {

    private static final long serialVersionUID = 1_0_0L;

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

    public static final String FILL = "fill";
    public static final String INITIAL = "initial";
    public static final String INHERIT = "inherit";

    protected static final String DEFAULT_VALUE = "100%";

    private static final List<String> PREDEFINED_CONSTANTS = Arrays
            .asList(INITIAL, INHERIT);

    private String cssValue;

    private CssLengthUnit topUnit;
    private CssLengthUnit rightUnit;
    private CssLengthUnit bottomUnit;
    private CssLengthUnit leftUnit;

    private Float top;
    private Float right;
    private Float bottom;
    private Float left;

    // This variable can contain either FILL value or null
    private String middle;

    /**
     * The {@code 1} will be set as the value
     */
    public BorderImageSlice() {
        setCssValue(DEFAULT_VALUE);
    }

    /**
     * @param cssValue
     *                     the css value to set.
     */
    public BorderImageSlice(final String cssValue) {
        setCssValue(cssValue);
    }

    /**
     * @param bottom
     *                   the {@code BorderBottomSlice} object from which the
     *                   cssValue to set.And, {@code null} will throw
     *                   {@code NullValueException}
     */
    public BorderImageSlice(final BorderImageSlice bottom) {
        if (bottom == null) {
            throw new NullValueException("bottom can not be null");
        }
        setCssValue(bottom.getCssValue());
    }

    /**
     * @param percent
     *                    the percentage value to set. The cssLengthUnit will
     *                    automatically set to %.
     * @since 1.0.0
     * @author WFF
     */
    public BorderImageSlice(final float percent) {
        setCssValue(String.valueOf(percent) + CssLengthUnit.PER);
    }

    /**
     * @param value
     * @param cssLengthUnit
     */
    public BorderImageSlice(final float value,
            final CssLengthUnit cssLengthUnit) {
        setCssValue(String.valueOf(value) + cssLengthUnit);
    }

    /**
     * @param percent
     *                    the percent to set
     * @since 1.0.0
     * @author WFF
     */
    public void setPercent(final float percent) {
        setCssValue(String.valueOf(percent) + CssLengthUnit.PER);
    }

    /*
     * (non-Javadoc)
     *
     * @see com.webfirmframework.wffweb.css.CssProperty#getCssName()
     *
     * @since 1.0.0
     *
     * @author WFF
     */
    @Override
    public String getCssName() {
        return CssNameConstants.BORDER_IMAGE_SLICE;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.webfirmframework.wffweb.css.CssProperty#getCssValue()
     *
     * @since 1.0.0
     *
     * @author WFF
     */
    @Override
    public String getCssValue() {
        return cssValue;
    }

    @Override
    public String toString() {
        return getCssName() + ": " + getCssValue();
    }

    /**
     * @param cssValue
     *                     the value should be in the format of
     *                     55px or 95%. {@code null}
     *                     is considered as an invalid value and it will throw
     *                     {@code NullValueException}.
     * @since 1.0.0
     * @author WFF
     */
    @Override
    public BorderImageSlice setCssValue(final String cssValue) {
        if (cssValue == null) {
            throw new NullValueException(
                    "null is an invalid value. The value format should be as for example 75px or 85%. Or, initial/inherit/medium/thin/thick.");
        } else if (StringUtil.isBlank(cssValue)) {
            throw new InvalidValueException(
                    "blank string is an invalid value. The value format should be as for example 75px or 85%. Or, initial/inherit/medium/thin/thick.");
        } else {

            final String previousCssValue = this.cssValue;
            final String previousMiddle = middle;

            final CssLengthUnit previousTopUnit = topUnit;
            final CssLengthUnit previousRightUnit = rightUnit;
            final CssLengthUnit previousBottomUnit = bottomUnit;
            final CssLengthUnit previousLeftUnit = leftUnit;

            final Float previousTop = top;
            final Float previousRight = right;
            final Float previousBottom = bottom;
            final Float previousLeft = left;
            try {
                final String trimmedCssValue = TagStringUtil
                        .toLowerCase(StringUtil.strip(cssValue));

                if (PREDEFINED_CONSTANTS.contains(trimmedCssValue)) {
                    this.cssValue = trimmedCssValue;
                    top = right = bottom = left = null;
                    topUnit = rightUnit = bottomUnit = leftUnit = null;
                    if (getStateChangeInformer() != null) {
                        getStateChangeInformer().stateChanged(this);
                    }
                    return this;
                }

                final String cssValueWithoutFill = trimmedCssValue.replace(FILL,
                        "");

                final int lengthOfFill = FILL.length();

                final int lengthWithFill = cssValueWithoutFill.length()
                        + lengthOfFill;

                final int lengthBeforeRemovingFill = trimmedCssValue.length();

                if ((lengthBeforeRemovingFill - lengthWithFill) > 0) {
                    throw new InvalidValueException("The given cssValue '"
                            + cssValue
                            + "' is invalid as it contains more that one 'fill' value.");
                }

                middle = lengthBeforeRemovingFill == lengthWithFill ? FILL
                        : null;

                final String convertedToSingleSpace = StringUtil
                        .convertToSingleSpace(
                                StringUtil.strip(cssValueWithoutFill));

                if (!StringUtil.isBlank(convertedToSingleSpace)) {

                    final String[] extractedSlices = StringUtil
                            .splitBySpace(convertedToSingleSpace);

                    if (extractedSlices.length == 1) {
                        final Object[] lengthValueAndUnitAll = CssLengthUtil
                                .getLengthValueAndUnit(extractedSlices[0]);
                        if (lengthValueAndUnitAll.length == 2) {
                            top = right = bottom = left = (Float) lengthValueAndUnitAll[0];
                            topUnit = rightUnit = bottomUnit = leftUnit = (CssLengthUnit) lengthValueAndUnitAll[1];
                        } else if (lengthValueAndUnitAll.length == 1) {
                            top = right = bottom = left = (Float) lengthValueAndUnitAll[0];
                            topUnit = rightUnit = bottomUnit = leftUnit = null;
                        } else {
                            throw new InvalidValueException("'"
                                    + extractedSlices[0] + "' is invalid in '"
                                    + cssValue + "'");
                        }

                    } else if (extractedSlices.length == 2) {

                        final Object[] lengthValueAndUnitTopBottom = CssLengthUtil
                                .getLengthValueAndUnit(extractedSlices[0]);

                        if (lengthValueAndUnitTopBottom.length == 2) {
                            top = bottom = (Float) lengthValueAndUnitTopBottom[0];
                            topUnit = bottomUnit = (CssLengthUnit) lengthValueAndUnitTopBottom[1];
                        } else if (lengthValueAndUnitTopBottom.length == 1) {
                            top = bottom = (Float) lengthValueAndUnitTopBottom[0];
                            topUnit = bottomUnit = null;
                        } else {
                            throw new InvalidValueException("'"
                                    + extractedSlices[0] + "' is invalid in '"
                                    + cssValue + "'");
                        }

                        final Object[] lengthValueAndUnitRightLeft = CssLengthUtil
                                .getLengthValueAndUnit(extractedSlices[1]);

                        if (lengthValueAndUnitRightLeft.length == 2) {
                            right = left = (Float) lengthValueAndUnitRightLeft[0];
                            rightUnit = leftUnit = (CssLengthUnit) lengthValueAndUnitRightLeft[1];
                        } else if (lengthValueAndUnitRightLeft.length == 1) {
                            right = left = (Float) lengthValueAndUnitRightLeft[0];
                            rightUnit = leftUnit = null;
                        } else {
                            throw new InvalidValueException("'"
                                    + extractedSlices[1] + "' is invalid in '"
                                    + cssValue + "'");
                        }

                    } else if (extractedSlices.length == 3) {

                        final Object[] lengthValueAndUnitTop = CssLengthUtil
                                .getLengthValueAndUnit(extractedSlices[0]);
                        if (lengthValueAndUnitTop.length == 2) {
                            top = (Float) lengthValueAndUnitTop[0];
                            topUnit = (CssLengthUnit) lengthValueAndUnitTop[1];
                        } else if (lengthValueAndUnitTop.length == 1) {
                            top = (Float) lengthValueAndUnitTop[0];
                            topUnit = null;
                        } else {
                            throw new InvalidValueException("'"
                                    + extractedSlices[0] + "' is invalid in '"
                                    + cssValue + "'");
                        }

                        final Object[] lengthValueAndUnitRightLeft = CssLengthUtil
                                .getLengthValueAndUnit(extractedSlices[1]);
                        if (lengthValueAndUnitRightLeft.length == 2) {
                            right = left = (Float) lengthValueAndUnitRightLeft[0];
                            rightUnit = leftUnit = (CssLengthUnit) lengthValueAndUnitRightLeft[1];
                        } else if (lengthValueAndUnitRightLeft.length == 1) {
                            right = left = (Float) lengthValueAndUnitRightLeft[0];
                            rightUnit = leftUnit = null;
                        } else {
                            throw new InvalidValueException("'"
                                    + extractedSlices[1] + "' is invalid in '"
                                    + cssValue + "'");
                        }

                        final Object[] lengthValueAndUnitBottom = CssLengthUtil
                                .getLengthValueAndUnit(extractedSlices[2]);
                        if (lengthValueAndUnitBottom.length == 2) {
                            bottom = (Float) lengthValueAndUnitBottom[0];
                            bottomUnit = (CssLengthUnit) lengthValueAndUnitBottom[1];
                        } else if (lengthValueAndUnitBottom.length == 1) {
                            bottom = (Float) lengthValueAndUnitBottom[0];
                            bottomUnit = null;
                        } else {
                            throw new InvalidValueException("'"
                                    + extractedSlices[2] + "' is invalid in '"
                                    + cssValue + "'");
                        }

                    } else if (extractedSlices.length == 4) {

                        final Object[] lengthValueAndUnitTop = CssLengthUtil
                                .getLengthValueAndUnit(extractedSlices[0]);
                        if (lengthValueAndUnitTop.length == 2) {
                            top = (Float) lengthValueAndUnitTop[0];
                            topUnit = (CssLengthUnit) lengthValueAndUnitTop[1];
                        } else if (lengthValueAndUnitTop.length == 1) {
                            top = (Float) lengthValueAndUnitTop[0];
                            topUnit = null;
                        } else {
                            throw new InvalidValueException("'"
                                    + extractedSlices[0] + "' is invalid in '"
                                    + cssValue + "'");
                        }

                        final Object[] lengthValueAndUnitRight = CssLengthUtil
                                .getLengthValueAndUnit(extractedSlices[1]);
                        if (lengthValueAndUnitRight.length == 2) {
                            right = (Float) lengthValueAndUnitRight[0];
                            rightUnit = (CssLengthUnit) lengthValueAndUnitRight[1];
                        } else if (lengthValueAndUnitRight.length == 1) {
                            right = (Float) lengthValueAndUnitRight[0];
                            rightUnit = null;
                        } else {
                            throw new InvalidValueException("'"
                                    + extractedSlices[1] + "' is invalid in '"
                                    + cssValue + "'");
                        }
                        final Object[] lengthValueAndUnitBottom = CssLengthUtil
                                .getLengthValueAndUnit(extractedSlices[2]);
                        if (lengthValueAndUnitBottom.length == 2) {
                            bottom = (Float) lengthValueAndUnitBottom[0];
                            bottomUnit = (CssLengthUnit) lengthValueAndUnitBottom[1];
                        } else if (lengthValueAndUnitBottom.length == 1) {
                            bottom = (Float) lengthValueAndUnitBottom[0];
                            bottomUnit = null;
                        } else {
                            throw new InvalidValueException("'"
                                    + extractedSlices[2] + "' is invalid in '"
                                    + cssValue + "'");
                        }

                        final Object[] lengthValueAndUnitLeft = CssLengthUtil
                                .getLengthValueAndUnit(extractedSlices[3]);
                        if (lengthValueAndUnitLeft.length == 2) {
                            left = (Float) lengthValueAndUnitLeft[0];
                            leftUnit = (CssLengthUnit) lengthValueAndUnitLeft[1];
                        } else if (lengthValueAndUnitLeft.length == 1) {
                            left = (Float) lengthValueAndUnitLeft[0];
                            leftUnit = null;
                        } else {
                            throw new InvalidValueException("'"
                                    + extractedSlices[3] + "' is invalid in '"
                                    + cssValue + "'");
                        }

                    } else {
                        throw new InvalidValueException(
                                "the given cssValue is invalid");
                    }
                }
                this.cssValue = getProducedCssValue(top, topUnit, right,
                        rightUnit, bottom, bottomUnit, left, leftUnit, middle);
                if (getStateChangeInformer() != null) {
                    getStateChangeInformer().stateChanged(this);
                }
            } catch (final InvalidValueException e) {
                this.cssValue = previousCssValue;
                middle = previousMiddle;

                topUnit = previousTopUnit;
                rightUnit = previousRightUnit;
                bottomUnit = previousBottomUnit;
                leftUnit = previousLeftUnit;

                top = previousTop;
                right = previousRight;
                bottom = previousBottom;
                left = previousLeft;
                throw e;
            }
        }
        return this;
    }

    /**
     * sets as {@code initial}
     *
     * @since 1.0.0
     * @author WFF
     */
    public void setAsInitial() {
        setCssValue(INITIAL);
    }

    /**
     * sets as {@code inherit}
     *
     * @since 1.0.0
     * @author WFF
     */
    public void setAsInherit() {
        setCssValue(INHERIT);
    }

    /**
     * adds the value {@code fill} in cssValue.
     *
     * @since 1.0.0
     * @author WFF
     */
    public void addFill() {
        middle = FILL;
        cssValue = cssValue.replace(FILL, "").concat(" ").concat(FILL);
        if (getStateChangeInformer() != null) {
            getStateChangeInformer().stateChanged(this);
        }
    }

    public void removeFill() {
        middle = null;
        cssValue = StringUtil.strip(cssValue.replace(FILL, ""));
        if (getStateChangeInformer() != null) {
            getStateChangeInformer().stateChanged(this);
        }
    }

    /**
     * sets the top, right, bottom and left in {@code BorderImageSlice}.
     *
     * @param top
     *                   the value for top. The value {@code fill} will be
     *                   assigned for null value.
     * @param right
     *                   the value for right. The value {@code auto} will be
     *                   assigned for null value.
     * @param bottom
     *                   the value for bottom. The value {@code auto} will be
     *                   assigned for null value.
     * @param left
     *                   the value for left. The value {@code auto} will be
     *                   assigned for null value.
     * @param unit
     *                   the {@code CssLengthUnit} for all of the given top,
     *                   right, bottom and left values.
     * @author WFF
     * @since 1.0.0
     */
    public void setBorderImageTopRightBottomLeft(final Float top,
            final Float right, final Float bottom, final Float left,
            final CssLengthUnit unit) {

        cssValue = getProducedCssValue(top, unit, right, unit, bottom, unit,
                left, unit, middle);

        this.top = top;
        this.right = right;
        this.bottom = bottom;
        this.left = left;
        topUnit = rightUnit = bottomUnit = leftUnit = unit;

        final StateChangeInformer<CssProperty> stateChangeInformer = getStateChangeInformer();
        if (stateChangeInformer != null) {
            stateChangeInformer.stateChanged(this);
        }
    }

    public void setBorderImageTopRightBottomLeft(final Float top,
            final CssLengthUnit topUnit, final Float right,
            final CssLengthUnit rightUnit, final Float bottom,
            final CssLengthUnit bottomUnit, final Float left,
            final CssLengthUnit leftUnit) {

        cssValue = getProducedCssValue(top, topUnit, right, rightUnit, bottom,
                bottomUnit, left, leftUnit, middle);

        this.top = top;
        this.right = right;
        this.bottom = bottom;
        this.left = left;

        this.topUnit = topUnit;
        this.rightUnit = rightUnit;
        this.bottomUnit = bottomUnit;
        this.leftUnit = leftUnit;

        final StateChangeInformer<CssProperty> stateChangeInformer = getStateChangeInformer();
        if (stateChangeInformer != null) {
            stateChangeInformer.stateChanged(this);
        }
    }

    /**
     * @param top
     *                the top to set
     * @author WFF
     * @since 1.0.0
     */
    public void setTop(final Float top, final CssLengthUnit topUnit) {
        cssValue = getProducedCssValue(top, topUnit, right, rightUnit, bottom,
                bottomUnit, left, leftUnit, middle);
        this.top = top;
        this.topUnit = topUnit;
        final StateChangeInformer<CssProperty> stateChangeInformer = getStateChangeInformer();
        if (stateChangeInformer != null) {
            stateChangeInformer.stateChanged(this);
        }
    }

    /**
     * @param right
     *                  the right to set
     * @author WFF
     * @param rightUnit
     * @since 1.0.0
     */
    public void setRight(final Float right, final CssLengthUnit rightUnit) {
        cssValue = getProducedCssValue(top, topUnit, right, rightUnit, bottom,
                bottomUnit, left, leftUnit, middle);
        this.right = right;
        this.rightUnit = rightUnit;
        final StateChangeInformer<CssProperty> stateChangeInformer = getStateChangeInformer();
        if (stateChangeInformer != null) {
            stateChangeInformer.stateChanged(this);
        }
    }

    /**
     * @param bottom
     *                   the bottom to set
     * @author WFF
     * @param bottomUnit
     * @since 1.0.0
     */
    public void setBottom(final Float bottom, final CssLengthUnit bottomUnit) {
        cssValue = getProducedCssValue(top, topUnit, right, rightUnit, bottom,
                bottomUnit, left, leftUnit, middle);
        this.bottom = bottom;
        this.bottomUnit = bottomUnit;
        final StateChangeInformer<CssProperty> stateChangeInformer = getStateChangeInformer();
        if (stateChangeInformer != null) {
            stateChangeInformer.stateChanged(this);
        }
    }

    /**
     * @param left
     *                 the left to set
     * @author WFF
     * @param leftUnit
     * @since 1.0.0
     */
    public void setLeft(final Float left, final CssLengthUnit leftUnit) {
        cssValue = getProducedCssValue(top, topUnit, right, rightUnit, bottom,
                bottomUnit, left, leftUnit, middle);
        this.left = left;
        this.leftUnit = leftUnit;
        final StateChangeInformer<CssProperty> stateChangeInformer = getStateChangeInformer();
        if (stateChangeInformer != null) {
            stateChangeInformer.stateChanged(this);
        }
    }

    /**
     * @return the top. It will return null for auto or the cssValue is
     *         predefined constant.
     * @author WFF
     * @since 1.0.0
     */
    public Float getTop() {
        return top;
    }

    /**
     * @return the topUnit
     * @author WFF
     * @since 1.0.0
     */
    public CssLengthUnit getTopUnit() {
        return topUnit;
    }

    /**
     * @return the right. It will return null for auto or the cssValue is
     *         predefined constant.
     * @author WFF
     * @since 1.0.0
     */
    public Float getRight() {
        return right;
    }

    /**
     * @return the rightUnit
     * @author WFF
     * @since 1.0.0
     */
    public CssLengthUnit getRightUnit() {
        return rightUnit;
    }

    /**
     * @return the bottom. It will return null for auto or the cssValue is
     *         predefined constant.
     * @author WFF
     * @since 1.0.0
     */
    public Float getBottom() {
        return bottom;
    }

    /**
     * @return the bottomUnit
     * @author WFF
     * @since 1.0.0
     */
    public CssLengthUnit getBottomUnit() {
        return bottomUnit;
    }

    /**
     * @return the left. It will return null for auto or the cssValue is
     *         predefined constant.
     * @author WFF
     * @since 1.0.0
     */
    public Float getLeft() {
        return left;
    }

    /**
     * @return the leftUnit
     * @author WFF
     * @since 1.0.0
     */
    public CssLengthUnit getLeftUnit() {
        return leftUnit;
    }

    /**
     *
     * @param top
     *                       the value for top. The value {@code auto} will be
     *                       assigned for null value.
     * @param topUnit
     * @param right
     *                       the value for right. The value {@code auto} will be
     *                       assigned for null value.
     * @param rightUnit
     * @param bottom
     *                       the value for bottom. The value {@code auto} will
     *                       be assigned for null value.
     * @param bottomUnit
     * @param left
     *                       the value for left. The value {@code auto} will be
     *                       assigned for null value.
     * @param leftUnit
     * @param middle
     *                       TODO
     * @author WFF
     * @return
     * @since 1.0.0
     */
    protected static String getProducedCssValue(final Float top,
            final CssLengthUnit topUnit, final Float right,
            final CssLengthUnit rightUnit, final Float bottom,
            final CssLengthUnit bottomUnit, final Float left,
            final CssLengthUnit leftUnit, final String middle) {

        if ((ObjectUtil.isEqual(top, right)
                && Objects.equals(topUnit, rightUnit))
                && (ObjectUtil.isEqual(right, bottom)
                        && ObjectUtil.isEqual(rightUnit, bottomUnit))
                && (ObjectUtil.isEqual(bottom, left)
                        && Objects.equals(bottomUnit, leftUnit))) {

            if (middle != null) {
                return (top != null && topUnit != null
                        ? String.valueOf(top).concat(topUnit.getUnit())
                        : top != null ? String.valueOf(top) : DEFAULT_VALUE)
                                .concat(" ").concat(middle);
            }

            return top != null && topUnit != null
                    ? String.valueOf(top).concat(topUnit.getUnit())
                    : top != null ? String.valueOf(top) : DEFAULT_VALUE;

        } else if ((ObjectUtil.isEqual(top, bottom)
                && Objects.equals(topUnit, bottomUnit))
                && (ObjectUtil.isEqual(right, left)
                        && Objects.equals(rightUnit, leftUnit))) {

            final StringBuilder cssValueBuilder = new StringBuilder();
            if (top != null) {
                cssValueBuilder.append(top);
                if (topUnit != null) {
                    cssValueBuilder.append(topUnit.getUnit());
                }
            }

            cssValueBuilder.append(' ');

            if (right != null) {
                cssValueBuilder.append(right);
                if (rightUnit != null) {
                    cssValueBuilder.append(rightUnit.getUnit());
                }
            }

            if (middle != null) {
                cssValueBuilder.append(' ').append(middle);
            }

            return cssValueBuilder.toString();
        } else if ((ObjectUtil.isEqual(right, left)
                && Objects.equals(rightUnit, leftUnit))) {
            final StringBuilder cssValueBuilder = new StringBuilder();
            if (top != null) {
                cssValueBuilder.append(top);
                if (topUnit != null) {
                    cssValueBuilder.append(topUnit.getUnit());
                }
            }

            cssValueBuilder.append(' ');

            if (right != null) {
                cssValueBuilder.append(right);
                if (rightUnit != null) {
                    cssValueBuilder.append(rightUnit.getUnit());
                }
            }

            cssValueBuilder.append(' ');

            if (bottom != null) {
                cssValueBuilder.append(bottom);
                if (bottomUnit != null) {
                    cssValueBuilder.append(bottomUnit.getUnit());
                }
            }

            if (middle != null) {
                cssValueBuilder.append(' ').append(middle);
            }

            return cssValueBuilder.toString();

        } else {
            final StringBuilder cssValueBuilder = new StringBuilder();
            if (top != null) {
                cssValueBuilder.append(top);
                if (topUnit != null) {
                    cssValueBuilder.append(topUnit.getUnit());
                }
            }

            cssValueBuilder.append(' ');

            if (right != null) {
                cssValueBuilder.append(right);
                if (rightUnit != null) {
                    cssValueBuilder.append(rightUnit.getUnit());
                }
            }

            cssValueBuilder.append(' ');

            if (bottom != null) {
                cssValueBuilder.append(bottom);
                if (bottomUnit != null) {
                    cssValueBuilder.append(bottomUnit.getUnit());
                }
            }

            cssValueBuilder.append(' ');

            if (left != null) {
                cssValueBuilder.append(left);
                if (leftUnit != null) {
                    cssValueBuilder.append(leftUnit.getUnit());
                }
            }

            if (middle != null) {
                cssValueBuilder.append(' ').append(middle);
            }

            return cssValueBuilder.toString();
        }
    }

    /**
     * validates if the given cssValue is valid for this class.
     *
     * @param cssValue
     *                     the value to check.
     * @return true if valid and false if invalid.
     * @author WFF
     * @since 1.0.0
     */
    public static boolean isValid(final String cssValue) {
        final String trimmedCssValue = StringUtil.convertToSingleSpace(
                TagStringUtil.toLowerCase(StringUtil.strip(cssValue)));

        if (PREDEFINED_CONSTANTS.contains(trimmedCssValue)) {
            return true;
        }

        final String[] cssValueParts = StringUtil.splitBySpace(trimmedCssValue);
        if (cssValueParts.length > 5) {
            return false;
        }

        if ((cssValue.length() - cssValue.replace(FILL, "").length()) > FILL
                .length()) {
            return false;
        }

        for (final String cssValuePart : cssValueParts) {
            final Object[] lengthValueAndUnit = CssLengthUtil
                    .getLengthValueAndUnit(cssValuePart);
            if (lengthValueAndUnit.length == 0 && !FILL.equals(cssValuePart)) {
                return false;
            }
        }
        return true;
    }

    /**
     * @return true if its cssValue is any of the values
     *          auto, initial or inherit.
     * @author WFF
     * @since 1.0.0
     */
    public boolean hasPredefinedConstantValue() {
        return PREDEFINED_CONSTANTS.contains(cssValue);
    }

}


/*
 * Copyright 2014-2019 Web Firm Framework
 *
 * 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.
 * @author WFF
 */
package com.webfirmframework.wffweb.util;

/**
 *
 * @author WFF
 * @since 1.0.0
 */
public final class TagStringUtil {

    private static final char[] UPPER_CASE_CHARS = CharsetUtil
            .getUpperCaseCharset(512);

    private static final char[] LOWER_CASE_CHARS = CharsetUtil
            .getLowerCaseCharset(512);

    private TagStringUtil() {
        throw new AssertionError();
    }

    /**
     * In html, the tag characters are supposed to use a limited number of
     * characters like 'a'-'z', 'A'-'Z', '0'-'9', '.', ',', '-' etc... only. *
     * This method gains performance than the native
     * {@code String.toLowerCase()} method.
     *
     * @param value
     *                  The String to lower case
     * @return The lower case string
     * @author WFF
     */
    public static String toLowerCase(final String value) {
        final char[] chars = value.toCharArray();

        for (int i = 0; i < chars.length; i++) {
            chars[i] = LOWER_CASE_CHARS[chars[i]];
        }

        return new String(chars);
    }

    /**
     * In html, the tag characters are supposed to use a limited number of
     * characters like 'a'-'z', 'A'-'Z', '0'-'9', '.', ',', '-' etc...<br>
     * This method gains performance than the native
     * {@code String.toUpperCase()} method.
     *
     * @param value
     *                  The String to lower case
     * @return The upper case string
     * @author WFF
     */
    public static String toUpperCase(final String value) {
        final char[] chars = value.toCharArray();

        for (int i = 0; i < chars.length; i++) {
            chars[i] = UPPER_CASE_CHARS[chars[i]];
        }

        return new String(chars);
    }

}