/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.model;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.ModelHints;
import com.liferay.portal.kernel.model.ModelHintsCallback;
import com.liferay.portal.kernel.model.ModelHintsConstants;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Tuple;
import com.liferay.portal.kernel.util.Validator;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* @author Brian Wing Shun Chan
* @author Tomas Polesovsky
* @author Raymond Augé
*/
public abstract class BaseModelHintsImpl implements ModelHints {
public void afterPropertiesSet() {
_hintCollections = new HashMap<>();
_defaultHints = new HashMap<>();
_modelFields = new HashMap<>();
_models = new TreeSet<>();
try {
Class<?> clazz = getClass();
ClassLoader classLoader = clazz.getClassLoader();
for (String config : getModelHintsConfigs()) {
if (config.startsWith("classpath*:")) {
String name = config.substring("classpath*:".length());
Enumeration<URL> enu = classLoader.getResources(name);
if (_log.isDebugEnabled() && !enu.hasMoreElements()) {
_log.debug("No resources found for " + name);
}
while (enu.hasMoreElements()) {
URL url = enu.nextElement();
if (_log.isDebugEnabled()) {
_log.debug("Loading " + name + " from " + url);
}
InputStream inputStream = url.openStream();
read(classLoader, url.toString(), inputStream);
}
}
else {
InputStream inputStream = classLoader.getResourceAsStream(
config);
if (inputStream == null) {
File file = new File(config);
if (!file.exists()) {
continue;
}
inputStream = new FileInputStream(file);
}
try (InputStream curInputStream = inputStream) {
read(classLoader, config, curInputStream);
}
}
}
}
catch (Exception e) {
_log.error(e, e);
}
}
@Override
public String buildCustomValidatorName(String validatorName) {
return validatorName.concat(StringPool.UNDERLINE).concat(
StringUtil.randomId());
}
@Override
public Map<String, String> getDefaultHints(String model) {
return _defaultHints.get(model);
}
@Override
public Object getFieldsElement(String model, String field) {
Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
model);
if (fields == null) {
return null;
}
Element fieldsEl = (Element)fields.get(field + _ELEMENTS_SUFFIX);
if (fieldsEl == null) {
return null;
}
else {
return fieldsEl;
}
}
@Override
public Map<String, String> getHints(String model, String field) {
Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
model);
if (fields == null) {
return null;
}
else {
return (Map<String, String>)fields.get(field + _HINTS_SUFFIX);
}
}
@Override
public int getMaxLength(String model, String field) {
Map<String, String> hints = getHints(model, field);
if (hints == null) {
return Integer.MAX_VALUE;
}
int maxLength = GetterUtil.getInteger(
ModelHintsConstants.TEXT_MAX_LENGTH);
maxLength = GetterUtil.getInteger(hints.get("max-length"), maxLength);
return maxLength;
}
public abstract ModelHintsCallback getModelHintsCallback();
public abstract String[] getModelHintsConfigs();
@Override
public List<String> getModels() {
return ListUtil.fromCollection(_models);
}
@Override
public Tuple getSanitizeTuple(String model, String field) {
Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
model);
if (fields == null) {
return null;
}
else {
return (Tuple)fields.get(field + _SANITIZE_SUFFIX);
}
}
@Override
public List<Tuple> getSanitizeTuples(String model) {
Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
model);
if (fields == null) {
return Collections.emptyList();
}
List<Tuple> sanitizeTuples = new ArrayList<>();
for (Map.Entry<String, Object> entry : fields.entrySet()) {
String key = entry.getKey();
if (key.endsWith(_SANITIZE_SUFFIX)) {
Tuple sanitizeTuple = (Tuple)entry.getValue();
sanitizeTuples.add(sanitizeTuple);
}
}
return sanitizeTuples;
}
public abstract SAXReader getSAXReader();
@Override
public String getType(String model, String field) {
Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
model);
if (fields == null) {
return null;
}
else {
return (String)fields.get(field + _TYPE_SUFFIX);
}
}
@Override
public List<Tuple> getValidators(String model, String field) {
Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
model);
if ((fields == null) ||
(fields.get(field + _VALIDATORS_SUFFIX) == null)) {
return null;
}
else {
return (List<Tuple>)fields.get(field + _VALIDATORS_SUFFIX);
}
}
@Override
public String getValue(
String model, String field, String name, String defaultValue) {
Map<String, String> hints = getHints(model, field);
if (hints == null) {
return defaultValue;
}
return GetterUtil.getString(hints.get(name), defaultValue);
}
@Override
public boolean hasField(String model, String field) {
Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
model);
if (fields == null) {
return false;
}
return fields.containsKey(field + _ELEMENTS_SUFFIX);
}
@Override
public boolean isCustomValidator(String validatorName) {
if (validatorName.equals("custom")) {
return true;
}
return false;
}
@Override
public boolean isLocalized(String model, String field) {
Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
model);
if (fields == null) {
return false;
}
Boolean localized = (Boolean)fields.get(field + _LOCALIZATION_SUFFIX);
if (localized != null) {
return localized;
}
else {
return false;
}
}
@Override
public void read(ClassLoader classLoader, InputStream inputStream)
throws Exception {
read(classLoader, null, inputStream);
}
@Override
public void read(ClassLoader classLoader, String source) throws Exception {
read(classLoader, source, classLoader.getResourceAsStream(source));
}
public void read(
ClassLoader classLoader, String source, InputStream inputStream)
throws Exception {
if (inputStream == null) {
if (_log.isWarnEnabled()) {
_log.warn("Cannot load " + source);
}
return;
}
else {
if (_log.isDebugEnabled()) {
_log.debug("Loading " + source);
}
}
SAXReader saxReader = getSAXReader();
Document document = saxReader.read(inputStream);
Element rootElement = document.getRootElement();
List<Element> rootElements = rootElement.elements("hint-collection");
for (Element hintCollectionElement : rootElements) {
String name = hintCollectionElement.attributeValue("name");
Map<String, String> hints = _hintCollections.get(name);
if (hints == null) {
hints = new HashMap<>();
_hintCollections.put(name, hints);
}
List<Element> hintElements = hintCollectionElement.elements("hint");
for (Element hintElement : hintElements) {
String hintName = hintElement.attributeValue("name");
String hintValue = hintElement.getText();
hints.put(hintName, hintValue);
}
}
rootElements = rootElement.elements("model");
for (Element modelElement : rootElements) {
String name = modelElement.attributeValue("name");
ModelHintsCallback modelHintsCallback = getModelHintsCallback();
modelHintsCallback.execute(classLoader, name);
Map<String, String> defaultHints = new HashMap<>();
_defaultHints.put(name, defaultHints);
Element defaultHintsElement = modelElement.element("default-hints");
if (defaultHintsElement != null) {
List<Element> hintElements = defaultHintsElement.elements(
"hint");
for (Element hintElement : hintElements) {
String hintName = hintElement.attributeValue("name");
String hintValue = hintElement.getText();
defaultHints.put(hintName, hintValue);
}
}
Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
name);
if (fields == null) {
fields = new LinkedHashMap<>();
_modelFields.put(name, fields);
}
_models.add(name);
List<Element> modelElements = modelElement.elements("field");
for (Element fieldElement : modelElements) {
String fieldName = fieldElement.attributeValue("name");
String fieldType = fieldElement.attributeValue("type");
boolean fieldLocalized = GetterUtil.getBoolean(
fieldElement.attributeValue("localized"));
Map<String, String> fieldHints = new HashMap<>();
fieldHints.putAll(defaultHints);
List<Element> fieldElements = fieldElement.elements(
"hint-collection");
for (Element hintCollectionElement : fieldElements) {
Map<String, String> hints = _hintCollections.get(
hintCollectionElement.attributeValue("name"));
fieldHints.putAll(hints);
}
fieldElements = fieldElement.elements("hint");
for (Element hintElement : fieldElements) {
String hintName = hintElement.attributeValue("name");
String hintValue = hintElement.getText();
fieldHints.put(hintName, hintValue);
}
Tuple fieldSanitize = null;
Element sanitizeElement = fieldElement.element("sanitize");
if (sanitizeElement != null) {
String contentType = sanitizeElement.attributeValue(
"content-type");
String modes = sanitizeElement.attributeValue("modes");
fieldSanitize = new Tuple(fieldName, contentType, modes);
}
Map<String, Tuple> fieldValidators = new TreeMap<>();
fieldElements = fieldElement.elements("validator");
for (Element validatorElement : fieldElements) {
String validatorName = validatorElement.attributeValue(
"name");
if (Validator.isNull(validatorName)) {
continue;
}
String validatorErrorMessage = GetterUtil.getString(
validatorElement.attributeValue("error-message"));
String validatorValue = GetterUtil.getString(
validatorElement.getText());
boolean customValidator = isCustomValidator(validatorName);
if (customValidator) {
validatorName = buildCustomValidatorName(validatorName);
}
Tuple fieldValidator = new Tuple(
fieldName, validatorName, validatorErrorMessage,
validatorValue, customValidator);
fieldValidators.put(validatorName, fieldValidator);
}
fields.put(fieldName + _ELEMENTS_SUFFIX, fieldElement);
fields.put(fieldName + _TYPE_SUFFIX, fieldType);
fields.put(fieldName + _LOCALIZATION_SUFFIX, fieldLocalized);
fields.put(fieldName + _HINTS_SUFFIX, fieldHints);
if (fieldSanitize != null) {
fields.put(fieldName + _SANITIZE_SUFFIX, fieldSanitize);
}
if (!fieldValidators.isEmpty()) {
fields.put(
fieldName + _VALIDATORS_SUFFIX,
ListUtil.fromMapValues(fieldValidators));
}
}
}
}
@Override
public String trimString(String model, String field, String value) {
if (value == null) {
return value;
}
int maxLength = getMaxLength(model, field);
if (value.length() > maxLength) {
return value.substring(0, maxLength);
}
else {
return value;
}
}
private static final String _ELEMENTS_SUFFIX = "_ELEMENTS";
private static final String _HINTS_SUFFIX = "_HINTS";
private static final String _LOCALIZATION_SUFFIX = "_LOCALIZATION";
private static final String _SANITIZE_SUFFIX = "_SANITIZE_SUFFIX";
private static final String _TYPE_SUFFIX = "_TYPE";
private static final String _VALIDATORS_SUFFIX = "_VALIDATORS";
private static final Log _log = LogFactoryUtil.getLog(
BaseModelHintsImpl.class);
private Map<String, Map<String, String>> _defaultHints;
private Map<String, Map<String, String>> _hintCollections;
private Map<String, Object> _modelFields;
private Set<String> _models;
}