blob: de25d9a6cc3a0de95a8d62c08abbd5c8dab20a72 [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*******************************************************************************/
package org.apache.ofbiz.minilang;
import java.util.HashSet;
import java.util.Set;
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.ScriptUtil;
import org.apache.ofbiz.base.util.UtilProperties;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* Mini-language validation.
*/
public final class MiniLangValidate {
public static final String module = MiniLangValidate.class.getName();
/**
* Tests <code>element</code> for invalid attribute names.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param validAttributeNames The valid attribute names
* @throws ValidationException If an invalid attribute name is found and <code>validation.level=strict</code>
*/
public static void attributeNames(SimpleMethod method, Element element, String... validAttributeNames) throws ValidationException {
Set<String> validNames = new HashSet<String>();
for (String name : validAttributeNames) {
validNames.add(name);
}
NamedNodeMap nnm = element.getAttributes();
for (int i = 0; i < nnm.getLength(); i++) {
String attributeName = nnm.item(i).getNodeName();
if (!validNames.contains(attributeName)) {
handleError("Attribute name \"" + attributeName + "\" is not valid.", method, element);
}
}
}
/**
* Returns <code>attributeValue</code> if it is not empty, else returns <code>defaultValue</code>.
* No <code>null</code> checks are performed.
*
* @param attributeValue
* @param defaultValue
* @return <code>attributeValue</code> if it is not empty, else returns <code>defaultValue</code>
*/
public static String checkAttribute(String attributeValue, String defaultValue) {
return attributeValue.isEmpty() ? defaultValue : attributeValue;
}
/**
* Tests <code>element</code> for invalid child elements.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param validChildElementNames The valid child element tag names
* @throws ValidationException If an invalid child element is found and <code>validation.level=strict</code>
*/
public static void childElements(SimpleMethod method, Element element, String... validChildElementNames) throws ValidationException {
Set<String> validNames = new HashSet<String>();
for (String name : validChildElementNames) {
validNames.add(name);
}
Node node = element.getFirstChild();
while (node != null) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) node;
if (!validNames.contains(childElement.getTagName())) {
handleError("Child element <" + childElement.getTagName() + "> is not valid.", method, element);
}
}
node = node.getNextSibling();
}
}
/**
* Tests if element attributes are constant type.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param attributeNames The attributes to test
* @throws ValidationException If an invalid attribute is found and <code>validation.level=strict</code>
*/
public static void constantAttributes(SimpleMethod method, Element element, String... attributeNames) throws ValidationException {
for (String name : attributeNames) {
String attributeValue = element.getAttribute(name);
if (!MiniLangUtil.isConstantAttribute(attributeValue)) {
handleError("Constant attribute \"" + name + "\" cannot contain an expression.", method, element);
}
}
}
/**
* Tests if element attributes are constant+expr type.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param attributeNames The attributes to test
* @throws ValidationException If an invalid attribute is found and <code>validation.level=strict</code>
*/
public static void constantPlusExpressionAttributes(SimpleMethod method, Element element, String... attributeNames) throws ValidationException {
for (String name : attributeNames) {
String attributeValue = element.getAttribute(name);
if (!MiniLangUtil.isConstantPlusExpressionAttribute(attributeValue)) {
handleError("Constant+expr attribute \"" + name + "\" is missing a constant value (expression-only constants are not allowed).", method, element);
}
if (MiniLangUtil.containsScript(attributeValue)) {
handleError("Constant+expr attribute \"" + name + "\" cannot contain a script (remove script).", method, element);
}
}
}
/**
* Tests <code>element</code> for a deprecated attribute.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param attributeName The name of the deprecated attribute
* @param fixInstruction Instructions to fix the deprecated attribute
* @throws ValidationException If the deprecated attribute is found and <code>validation.level=strict</code>
*/
public static void deprecatedAttribute(SimpleMethod method, Element element, String attributeName, String fixInstruction) throws ValidationException {
String attributeValue = element.getAttribute(attributeName);
if (attributeValue.length() > 0) {
handleError("Attribute \"" + attributeName + "\" is deprecated (" + fixInstruction + ")", method, element);
}
}
/**
* Tests if element attributes are expression type.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param attributeNames The attributes to test
* @throws ValidationException If an invalid attribute is found and <code>validation.level=strict</code>
*/
public static void expressionAttributes(SimpleMethod method, Element element, String... attributeNames) throws ValidationException {
for (String name : attributeNames) {
String attributeValue = element.getAttribute(name);
if (attributeValue.length() > 0) {
if (attributeValue.startsWith("${") && attributeValue.endsWith("}")) {
attributeValue = attributeValue.substring(2, attributeValue.length() - 1);
}
if (MiniLangUtil.containsScript(attributeValue)) {
handleError("Expression attribute \"" + name + "\" cannot contain a script (remove script).", method, element);
}
}
}
}
/**
* Handles a Mini-language validation error.
*
* @param errorMessage The error message
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> that contains the error
* @throws ValidationException If <code>validation.level=strict</code>, otherwise a warning is logged
*/
public static void handleError(String errorMessage, SimpleMethod method, Element element) throws ValidationException {
ValidationException e = new ValidationException(errorMessage, method, element);
if (strictOn()) {
throw e;
} else {
Debug.logWarning(e.getMessage(), module);
}
}
/**
* Returns <code>true</code> if <code>validation.level=lenient</code>.
*
* @return <code>true</code> if <code>validation.level=lenient</code>
*/
public static boolean lenientOn() {
return "lenient".equals(UtilProperties.getPropertyValue("minilang", "validation.level"));
}
/**
* Tests <code>element</code> for child elements.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @throws ValidationException If a child element is found and <code>validation.level=strict</code>
*/
public static void noChildElements(SimpleMethod method, Element element) throws ValidationException {
Node node = element.getFirstChild();
while (node != null) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) node;
handleError("Child element <" + childElement.getTagName() + "> is not valid.", method, element);
}
node = node.getNextSibling();
}
}
/**
* Tests <code>element</code> for any one required attribute from a set of attribute names.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param attributeNames The required attribute names
* @throws ValidationException If none of the required attributes are found and <code>validation.level=strict</code>
*/
public static void requireAnyAttribute(SimpleMethod method, Element element, String... attributeNames) throws ValidationException {
StringBuilder sb = new StringBuilder();
for (String name : attributeNames) {
String attributeValue = element.getAttribute(name);
if (attributeValue.length() > 0) {
return;
}
if (sb.length() > 0) {
sb.append(" ");
}
sb.append("\"").append(name).append("\"");
}
handleError("Element must include one of " + sb + " attributes.", method, element);
}
/**
* Tests <code>element</code> for any one required child element from a set of tag names.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param elementNames The required child element tag names
* @throws ValidationException If none of the required child elements are found and <code>validation.level=strict</code>
*/
public static void requireAnyChildElement(SimpleMethod method, Element element, String... elementNames) throws ValidationException {
Set<String> childElementNames = new HashSet<String>();
Node node = element.getFirstChild();
while (node != null) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) node;
childElementNames.add(childElement.getTagName());
}
node = node.getNextSibling();
}
StringBuilder sb = new StringBuilder();
for (String name : elementNames) {
if (childElementNames.contains(name)) {
return;
}
if (sb.length() > 0) {
sb.append(", ");
}
sb.append("<").append(name).append(">");
}
handleError("Element must include one of " + sb + " child elements.", method, element);
}
/**
* Tests <code>element</code> for required attributes.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param attributeNames The required attribute names
* @throws ValidationException If any of the required attributes are not found and <code>validation.level=strict</code>
*/
public static void requiredAttributes(SimpleMethod method, Element element, String... attributeNames) throws ValidationException {
for (String name : attributeNames) {
String attributeValue = element.getAttribute(name);
if (attributeValue.length() == 0) {
handleError("Required attribute \"" + name + "\" is missing.", method, element);
}
}
}
/**
* Tests <code>element</code> for required child elements.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param elementNames The required child element tag names
* @throws ValidationException If any of the required child elements are not found and <code>validation.level=strict</code>
*/
public static void requiredChildElements(SimpleMethod method, Element element, String... elementNames) throws ValidationException {
Set<String> childElementNames = new HashSet<String>();
Node node = element.getFirstChild();
while (node != null) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) node;
childElementNames.add(childElement.getTagName());
}
node = node.getNextSibling();
}
for (String name : elementNames) {
if (!childElementNames.contains(name)) {
handleError("Required child element <" + name + "> is missing.", method, element);
}
}
}
/**
* Tests if element attributes are script type.
*
* @param method The <code>&lt;simple-method&gt;</code> that contains <code>element</code>
* @param element The <code>element</code> to test
* @param attributeNames The attributes to test
* @throws ValidationException If an invalid attribute is found and <code>validation.level=strict</code>
*/
public static void scriptAttributes(SimpleMethod method, Element element, String... attributeNames) throws ValidationException {
for (String name : attributeNames) {
String attributeValue = element.getAttribute(name).trim();
if (attributeValue.length() > 0) {
if (attributeValue.startsWith("${") && attributeValue.endsWith("}")) {
handleError("Script attribute \"" + name + "\" enclosed in \"${}\" (remove enclosing ${}).", method, element);
}
boolean scriptFound = false;
for (String scriptName : ScriptUtil.SCRIPT_NAMES) {
String scriptPrefix = scriptName.concat(":");
if (attributeValue.contains(scriptPrefix)) {
scriptFound = true;
break;
}
}
if (!scriptFound) {
handleError("Script attribute \"" + name + "\" does not contain a script.", method, element);
}
}
}
}
/**
* Returns <code>true</code> if <code>validation.level=strict</code>.
*
* @return <code>true</code> if <code>validation.level=strict</code>
*/
public static boolean strictOn() {
return "strict".equals(UtilProperties.getPropertyValue("minilang", "validation.level"));
}
/**
* Returns <code>true</code> if <code>validation.level</code> is set to lenient or strict.
*
* @return <code>true</code> if <code>validation.level</code> is set to lenient or strict
*/
public static boolean validationOn() {
return !"none".equals(UtilProperties.getPropertyValue("minilang", "validation.level"));
}
private MiniLangValidate() {}
}