DataPropertyUtil.java

/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2014 wcm.io
 * %%
 * 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.
 * #L%
 */
package io.wcm.handler.richtext.impl;

import java.util.regex.Pattern;

import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * Converts case sensitive headless camel case property names to case insensitive HTML5 data property names
 * and vice versa.
 */
public final class DataPropertyUtil {

  private static final String HTML5_DATA_PREFIX = "data-";
  private static final Pattern HEADLESS_CAMEL_CASE_NAME_PATTERN = Pattern.compile("^[a-z][a-zA-Z0-9]*$");
  private static final Pattern HTML5_DATA_NAME_PATTERN = Pattern.compile("^data\\-[a-z][a-z0-9\\-]*$");

  private DataPropertyUtil() {
    // static methods only
  }

  /**
   * Converts a headless camel case property name to a HTML5 data attribute name including "data-" prefix.
   * @param headlessCamelCaseName Headless camel case name
   * @return HTML5 data attribute name
   * @throws IllegalArgumentException If parameter name is not valid
   */
  public static String toHtml5DataName(String headlessCamelCaseName) {
    if (StringUtils.isEmpty(headlessCamelCaseName)) {
      throw new IllegalArgumentException("Property name is empty.");
    }
    if (!isHeadlessCamelCaseName(headlessCamelCaseName)) {
      throw new IllegalArgumentException("This is not a valid headless camel case property name: " + headlessCamelCaseName);
    }

    StringBuilder html5DataName = new StringBuilder(HTML5_DATA_PREFIX);
    for (int i = 0; i < headlessCamelCaseName.length(); i++) {
      char c = headlessCamelCaseName.charAt(i);
      if (CharUtils.isAsciiAlphaUpper(c)) {
        html5DataName.append('-');
      }
      html5DataName.append(Character.toLowerCase(c));
    }

    return html5DataName.toString();
  }

  /**
   * Converts a HTML5 data attribute name including "data-" prefix to a headless camel case name.
   * @param html5DataName Html5 data attribute name
   * @return Headless camel case name
   * @throws IllegalArgumentException If parameter name is not valid
   */
  public static String toHeadlessCamelCaseName(String html5DataName) {
    if (StringUtils.isEmpty(html5DataName)) {
      throw new IllegalArgumentException("Property name is empty.");
    }
    if (!isHtml5DataName(html5DataName)) {
      throw new IllegalArgumentException("This is not a valid HTML5 data property name: " + html5DataName);
    }

    String html5DataNameWithoutSuffix = StringUtils.substringAfter(html5DataName, HTML5_DATA_PREFIX);
    StringBuilder headlessCamelCaseName = new StringBuilder();
    boolean upperCaseNext = false;
    for (int i = 0; i < html5DataNameWithoutSuffix.length(); i++) {
      char c = html5DataNameWithoutSuffix.charAt(i);
      if (c == '-') {
        upperCaseNext = true;
      }
      else if (upperCaseNext) {
        headlessCamelCaseName.append(Character.toUpperCase(c));
        upperCaseNext = false;
      }
      else {
        headlessCamelCaseName.append(c);
      }
    }

    return headlessCamelCaseName.toString();
  }

  /**
   * @param name Property name
   * @return true if property is a valid headless camel case name which can be converted to a HTML5 data property name.
   */
  public static boolean isHeadlessCamelCaseName(String name) {
    if (StringUtils.isEmpty(name)) {
      return false;
    }
    return HEADLESS_CAMEL_CASE_NAME_PATTERN.matcher(name).matches();
  }

  /**
   * @param name Property name
   * @return true if property name starts with "data-" prefix, and has only lowercase, number or hyphen chars.
   */
  public static boolean isHtml5DataName(String name) {
    if (StringUtils.isEmpty(name)) {
      return false;
    }
    return HTML5_DATA_NAME_PATTERN.matcher(name).matches();
  }

}