| /******************************************************************************* |
| * 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><simple-method></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><simple-method></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><simple-method></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><simple-method></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><simple-method></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><simple-method></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><simple-method></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><simple-method></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><simple-method></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><simple-method></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><simple-method></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><simple-method></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><simple-method></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() {} |
| |
| } |