| /* |
| * 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 com.opensymphony.xwork2.validator; |
| |
| import com.opensymphony.xwork2.ActionContext; |
| import com.opensymphony.xwork2.ObjectFactory; |
| import com.opensymphony.xwork2.config.ConfigurationException; |
| import com.opensymphony.xwork2.config.providers.XmlHelper; |
| import com.opensymphony.xwork2.inject.Inject; |
| import com.opensymphony.xwork2.util.DomHelper; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.logging.log4j.LogManager; |
| import org.w3c.dom.*; |
| import org.xml.sax.InputSource; |
| |
| import java.io.InputStream; |
| import java.util.*; |
| |
| /** |
| * Parse the validation file. (eg. MyAction-validation.xml, MyAction-actionAlias-validation.xml) |
| * to return a List of ValidatorConfig encapsulating the validator information. |
| * |
| * @author Jason Carreira |
| * @author James House |
| * @author tm_jee ( tm_jee (at) yahoo.co.uk ) |
| * @author Rob Harrop |
| * @author Rene Gielen |
| * @author Martin Gilday |
| * |
| * @see com.opensymphony.xwork2.validator.ValidatorConfig |
| */ |
| public class DefaultValidatorFileParser implements ValidatorFileParser { |
| |
| private static Logger LOG = LogManager.getLogger(DefaultValidatorFileParser.class); |
| |
| static final String DEFAULT_MULTI_TEXTVALUE_SEPARATOR = " "; |
| static final String MULTI_TEXTVALUE_SEPARATOR_CONFIG_KEY = "xwork.validatorfileparser.multi_textvalue_separator"; |
| |
| private ObjectFactory objectFactory; |
| private String multiTextvalueSeparator=DEFAULT_MULTI_TEXTVALUE_SEPARATOR; |
| |
| @Inject(value=MULTI_TEXTVALUE_SEPARATOR_CONFIG_KEY, required = false) |
| public void setMultiTextvalueSeparator(String type) { |
| multiTextvalueSeparator = type; |
| } |
| |
| public String getMultiTextvalueSeparator() { |
| return multiTextvalueSeparator; |
| } |
| |
| @Inject |
| public void setObjectFactory(ObjectFactory fac) { |
| this.objectFactory = fac; |
| } |
| |
| public List<ValidatorConfig> parseActionValidatorConfigs(ValidatorFactory validatorFactory, InputStream is, final String resourceName) { |
| List<ValidatorConfig> validatorCfgs = new ArrayList<>(); |
| |
| InputSource in = new InputSource(is); |
| in.setSystemId(resourceName); |
| |
| Map<String, String> dtdMappings = new HashMap<>(); |
| dtdMappings.put("-//Apache Struts//XWork Validator 1.0//EN", "xwork-validator-1.0.dtd"); |
| dtdMappings.put("-//Apache Struts//XWork Validator 1.0.2//EN", "xwork-validator-1.0.2.dtd"); |
| dtdMappings.put("-//Apache Struts//XWork Validator 1.0.3//EN", "xwork-validator-1.0.3.dtd"); |
| dtdMappings.put("-//Apache Struts//XWork Validator Config 1.0//EN", "xwork-validator-config-1.0.dtd"); |
| |
| Document doc = DomHelper.parse(in, dtdMappings); |
| |
| if (doc != null) { |
| NodeList fieldNodes = doc.getElementsByTagName("field"); |
| |
| // BUG: xw-305: Let validator be parsed first and hence added to |
| // the beginning of list and therefore evaluated first, so short-circuting |
| // it will not cause field-level validator to be kicked off. |
| { |
| NodeList validatorNodes = doc.getElementsByTagName("validator"); |
| addValidatorConfigs(validatorFactory, validatorNodes, new HashMap<String, Object>(), validatorCfgs); |
| } |
| |
| for (int i = 0; i < fieldNodes.getLength(); i++) { |
| Element fieldElement = (Element) fieldNodes.item(i); |
| String fieldName = fieldElement.getAttribute("name"); |
| Map<String, Object> extraParams = new HashMap<String, Object>(); |
| extraParams.put("fieldName", fieldName); |
| |
| NodeList validatorNodes = fieldElement.getElementsByTagName("field-validator"); |
| addValidatorConfigs(validatorFactory, validatorNodes, extraParams, validatorCfgs); |
| } |
| } |
| |
| return validatorCfgs; |
| } |
| |
| |
| public void parseValidatorDefinitions(Map<String, String> validators, InputStream is, String resourceName) { |
| |
| InputSource in = new InputSource(is); |
| in.setSystemId(resourceName); |
| |
| Map<String, String> dtdMappings = new HashMap<>(); |
| dtdMappings.put("-//Apache Struts//XWork Validator Config 1.0//EN", "xwork-validator-config-1.0.dtd"); |
| dtdMappings.put("-//Apache Struts//XWork Validator Definition 1.0//EN", "xwork-validator-definition-1.0.dtd"); |
| |
| Document doc = DomHelper.parse(in, dtdMappings); |
| |
| if (doc != null) { |
| NodeList nodes = doc.getElementsByTagName("validator"); |
| |
| for (int i = 0; i < nodes.getLength(); i++) { |
| Element validatorElement = (Element) nodes.item(i); |
| String name = validatorElement.getAttribute("name"); |
| String className = validatorElement.getAttribute("class"); |
| |
| try { |
| // catch any problems here |
| objectFactory.buildValidator(className, new HashMap<String, Object>(), ActionContext.getContext().getContextMap()); |
| validators.put(name, className); |
| } catch (Exception e) { |
| throw new ConfigurationException("Unable to load validator class " + className, e, validatorElement); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Extract trimmed text value from the given DOM element, ignoring XML comments. Appends all CharacterData nodes |
| * and EntityReference nodes into a single String value, excluding Comment nodes. |
| * This method is based on a method originally found in DomUtils class of Springframework. |
| * |
| * @param valueEle the given DOM element |
| * @return the text value |
| * |
| * @see org.w3c.dom.CharacterData |
| * @see org.w3c.dom.EntityReference |
| * @see org.w3c.dom.Comment |
| */ |
| public String getTextValue(Element valueEle) { |
| StringBuilder value = new StringBuilder(); |
| NodeList nl = valueEle.getChildNodes(); |
| boolean firstCDataFound = false; |
| for (int i = 0; i < nl.getLength(); i++) { |
| Node item = nl.item(i); |
| if ((item instanceof CharacterData && !(item instanceof Comment)) || item instanceof EntityReference) { |
| final String nodeValue = item.getNodeValue(); |
| if (nodeValue != null) { |
| if (firstCDataFound) { |
| value.append(getMultiTextvalueSeparator()); |
| } else { |
| firstCDataFound = true; |
| } |
| value.append(nodeValue.trim()); |
| } |
| } |
| } |
| return value.toString().trim(); |
| } |
| |
| private void addValidatorConfigs(ValidatorFactory factory, NodeList validatorNodes, Map<String, Object> extraParams, List<ValidatorConfig> validatorCfgs) { |
| for (int j = 0; j < validatorNodes.getLength(); j++) { |
| Element validatorElement = (Element) validatorNodes.item(j); |
| String validatorType = validatorElement.getAttribute("type"); |
| Map<String, Object> params = new HashMap<String, Object>(extraParams); |
| |
| params.putAll(XmlHelper.getParams(validatorElement)); |
| |
| // ensure that the type is valid... |
| try { |
| factory.lookupRegisteredValidatorType(validatorType); |
| } catch (IllegalArgumentException ex) { |
| throw new ConfigurationException("Invalid validation type: " + validatorType, validatorElement); |
| } |
| |
| ValidatorConfig.Builder vCfg = new ValidatorConfig.Builder(validatorType) |
| .addParams(params) |
| .location(DomHelper.getLocationObject(validatorElement)) |
| .shortCircuit(Boolean.valueOf(validatorElement.getAttribute("short-circuit"))); |
| |
| NodeList messageNodes = validatorElement.getElementsByTagName("message"); |
| Element messageElement = (Element) messageNodes.item(0); |
| |
| final Node defaultMessageNode = messageElement.getFirstChild(); |
| String defaultMessage = (defaultMessageNode == null) ? "" : defaultMessageNode.getNodeValue(); |
| vCfg.defaultMessage(defaultMessage); |
| |
| Map<String, String> messageParams = XmlHelper.getParams(messageElement); |
| String key = messageElement.getAttribute("key"); |
| |
| |
| if ((key != null) && (key.trim().length() > 0)) { |
| vCfg.messageKey(key); |
| |
| // Get the default message when pattern 2 is used. We are only interested in the |
| // i18n message parameters when an i18n message key is specified. |
| // pattern 1: |
| // <message key="someKey">Default message</message> |
| // pattern 2: |
| // <message key="someKey"> |
| // <param name="1">'param1'</param> |
| // <param name="2">'param2'</param> |
| // <param name="defaultMessage>The Default Message</param> |
| // </message> |
| |
| if (messageParams.containsKey("defaultMessage")) { |
| vCfg.defaultMessage(messageParams.get("defaultMessage")); |
| } |
| |
| // Sort the message param. those with keys as '1', '2', '3' etc. (numeric values) |
| // are i18n message parameter, others are excluded. |
| TreeMap<Integer, String> sortedMessageParameters = new TreeMap<Integer, String>(); |
| for (Map.Entry<String, String> messageParamEntry : messageParams.entrySet()) { |
| |
| try { |
| int _order = Integer.parseInt(messageParamEntry.getKey()); |
| sortedMessageParameters.put(_order, messageParamEntry.getValue()); |
| } |
| catch (NumberFormatException e) { |
| // ignore if its not numeric. |
| } |
| } |
| vCfg.messageParams(sortedMessageParameters.values().toArray(new String[sortedMessageParameters.values().size()])); |
| } else { |
| if (messageParams != null && (messageParams.size() > 0)) { |
| // we are i18n message parameters defined but no i18n message, |
| // let's warn the user. |
| if (LOG.isWarnEnabled()) { |
| LOG.warn("validator of type ["+validatorType+"] have i18n message parameters defined but no i18n message key, it's parameters will be ignored"); |
| } |
| } |
| } |
| |
| validatorCfgs.add(vCfg.build()); |
| } |
| } |
| } |