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

io.wcm.maven.plugins.i18n.SlingI18nMap Maven / Gradle / Ivy

There is a newer version: 1.3.0
Show newest version
/*
 * #%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.maven.plugins.i18n;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import com.google.common.collect.ImmutableList;

/**
 * Helper class integrating i18n JSON generation into a sorted map.
 */
class SlingI18nMap {

  private static final String JCR_LANGUAGE = "language";
  private static final List JCR_MIX_LANGUAGE = ImmutableList.of("mix:language");
  private static final String JCR_MIXIN_TYPES = "mixinTypes";
  private static final String JCR_NODETYPE_FOLDER = "nt:folder";
  private static final String JCR_PRIMARY_TYPE = "primaryType";

  private static final String SLING_KEY = "key";
  private static final String SLING_MESSAGE = "message";
  private static final List SLING_MESSAGE_MIXIN_TYPE = ImmutableList.of("sling:Message");

  private static final Namespace NAMESPACE_SLING = Namespace.getNamespace("sling", "http://sling.apache.org/jcr/sling/1.0");
  private static final Namespace NAMESPACE_JCR = Namespace.getNamespace("jcr", "http://www.jcp.org/jcr/1.0");
  private static final Namespace NAMESPACE_MIX = Namespace.getNamespace("mix", "http://www.jcp.org/jcr/mix/1.0");
  private static final Namespace NAMESPACE_NT = Namespace.getNamespace("nt", "http://www.jcp.org/jcr/nt/1.0");

  private String languageKey;
  private final SortedMap properties;

  /**
   * @param languageKey Language key
   */
  public SlingI18nMap(String languageKey, Map properties) {
    this.languageKey = languageKey;
    this.properties = new TreeMap<>(properties);
  }

  /**
   * Build i18n resource JSON in Sling i18n Message format.
   * @return JSON
   * @throws JSONException
   */
  public String getI18nJsonString() throws JSONException {
    return buildI18nJson().toString(2);
  }

  private JSONObject buildI18nJson() throws JSONException {

    // get root
    JSONObject jsonDocument = getMixLanguageJsonDocument();

    // add entries
    for (Entry entry : properties.entrySet()) {
      String key = entry.getKey();
      String escapedKey = validName(key);
      JSONObject value = getJsonI18nValue(key, entry.getValue(), !StringUtils.equals(key, escapedKey));

      jsonDocument.put(escapedKey, value);
    }

    // return result
    return jsonDocument;
  }

  private JSONObject getMixLanguageJsonDocument() throws JSONException {
    JSONObject root = new JSONObject();

    // add boiler plate
    root.put("jcr:" + JCR_PRIMARY_TYPE, JCR_NODETYPE_FOLDER);
    root.put("jcr:" + JCR_MIXIN_TYPES, JCR_MIX_LANGUAGE);

    // add language
    root.put("jcr:" + JCR_LANGUAGE, languageKey);

    return root;
  }

  private JSONObject getJsonI18nValue(String key, String value, boolean generatedKeyProperty) throws JSONException {
    JSONObject valueNode = new JSONObject();

    // add boiler plate
    valueNode.put("jcr:" + JCR_PRIMARY_TYPE, JCR_NODETYPE_FOLDER);
    valueNode.put("jcr:" + JCR_MIXIN_TYPES, SLING_MESSAGE_MIXIN_TYPE);

    // add extra key attribute
    if (generatedKeyProperty) {
      valueNode.put("sling:" + SLING_KEY, key);
    }

    // add actual i18n value
    valueNode.put("sling:" + SLING_MESSAGE, value);

    return valueNode;
  }

  /**
   * Build i18n resource XML in Sling i18n Message format.
   * @return XML
   */
  public String getI18nXmlString() {
    Format format = Format.getPrettyFormat();
    XMLOutputter outputter = new XMLOutputter(format);
    return outputter.outputString(buildI18nXml());
  }

  private Document buildI18nXml() {

    // get root
    Document xmlDocument = getMixLanguageXmlDocument();

    // add entries
    for (Entry entry : properties.entrySet()) {
      String key = entry.getKey();
      String escapedKey = validName(key);
      Element value = getXmlI18nValue(escapedKey, key, entry.getValue(), !StringUtils.equals(key, escapedKey));

      xmlDocument.getRootElement().addContent(value);
    }

    // return result
    return xmlDocument;
  }

  private Document getMixLanguageXmlDocument() {
    Document doc = new Document();
    Element root = new Element("root", NAMESPACE_JCR);
    root.addNamespaceDeclaration(NAMESPACE_JCR);
    root.addNamespaceDeclaration(NAMESPACE_MIX);
    root.addNamespaceDeclaration(NAMESPACE_NT);
    root.addNamespaceDeclaration(NAMESPACE_SLING);
    doc.setRootElement(root);

    // add boiler plate
    root.setAttribute(JCR_PRIMARY_TYPE, JCR_NODETYPE_FOLDER, NAMESPACE_JCR);
    root.setAttribute(JCR_MIXIN_TYPES, "[" + StringUtils.join(JCR_MIX_LANGUAGE, ",") + "]", NAMESPACE_JCR);

    // add language
    root.setAttribute(JCR_LANGUAGE, languageKey, NAMESPACE_JCR);

    return doc;
  }

  private Element getXmlI18nValue(String escapedKey, String key, String value, boolean generatedKeyProperty) {
    Element valueNode = new Element(escapedKey);

    // add boiler plate
    valueNode.setAttribute(JCR_PRIMARY_TYPE, JCR_NODETYPE_FOLDER, NAMESPACE_JCR);
    valueNode.setAttribute(JCR_MIXIN_TYPES, "[" + StringUtils.join(SLING_MESSAGE_MIXIN_TYPE, ",") + "]", NAMESPACE_JCR);

    // add extra key attribute
    if (generatedKeyProperty) {
      valueNode.setAttribute(SLING_KEY, key, NAMESPACE_SLING);
    }

    // add actual i18n value
    valueNode.setAttribute(SLING_MESSAGE, value, NAMESPACE_SLING);

    return valueNode;
  }

  /**
   * Creates a valid node name. Replaces all chars not in a-z, A-Z and 0-9 or '_', '.' with '-'.
   * @param value String to be labelized.
   * @return The labelized string.
   */
  private static String validName(String value) {

    // replace some special chars first
    String text = value;
    text = StringUtils.replace(text, "ä", "ae");
    text = StringUtils.replace(text, "ö", "oe");
    text = StringUtils.replace(text, "ü", "ue");
    text = StringUtils.replace(text, "ß", "ss");

    // replace all invalid chars
    StringBuilder sb = new StringBuilder(text);
    for (int i = 0; i < sb.length(); i++) {
      char ch = sb.charAt(i);
      if (!((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')
          || (ch == '_') || (ch == '.'))) {
        ch = '-';
        sb.setCharAt(i, ch);
      }
    }
    return sb.toString();
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy