/*************************GO-LICENSE-START*********************************
* Copyright 2014 ThoughtWorks, Inc.
*
* 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.
*************************GO-LICENSE-END***********************************/
package com.thoughtworks.go.util;
import org.apache.commons.lang.StringUtils;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class XsdErrorTranslator extends DefaultHandler {
private boolean validationError = false;
private SAXParseException saxParseException = null;
static List<MappingEntry> errorMapping = new ArrayList<>();
private static final int NONE = 0;
private static final int CAPITALIZE = 1;
private static final int HUMANIZE = 2;
private static final int REMOVE_TYPE_SUFFIX = 2;
static {
addMapping(
"cvc-attribute.3: The value '(.*)' of attribute '(.*)' on element '(.*)' is not valid with respect to its type, '(.*)'",
"\"{0}\" is invalid for {2} {1}",
NONE, NONE, CAPITALIZE);
addMapping(
"cvc-complex-type.4: Attribute '(.*)' must appear on element '(.*)'.",
"\"{0}\" is required for {1}",
HUMANIZE | CAPITALIZE , CAPITALIZE);
//Add more things to group 4 apart from Mingle. This is to handle xsd inline element type.
addMapping(
"cvc-pattern-valid: Value '(.*)' is not facet-valid with respect to pattern 'https\\?://.+' for type '#AnonType_siteUrlserverAttributeGroup'.",
"siteUrl \"{0}\" is invalid. It must start with ‘http://’ or ‘https://’", NONE);
addMapping(
"cvc-pattern-valid: Value '(.*)' is not facet-valid with respect to pattern 'https://.+' for type '#AnonType_secureSiteUrlserverAttributeGroup'.",
"secureSiteUrl \"{0}\" is invalid. It must be a secure URL (should start with ‘https://’)", NONE) ;
addMapping(
"cvc-pattern-valid: Value '(.*)' is not facet-valid with respect to pattern 'https://.+' for type '#AnonType_urlluauType'.",
"url \"{0}\" is invalid. It must be a secure URL (should start with ‘https://’)", NONE) ;
addMapping(
"cvc-pattern-valid: Value '(.*)' is not facet-valid with respect to pattern '(.*)' for type '#AnonType_(.*?)(mingle)Type'.",
"{2} in {3} is invalid. \"{0}\" should conform to the pattern - {1}",
NONE, NONE, HUMANIZE | CAPITALIZE | REMOVE_TYPE_SUFFIX, CAPITALIZE) ;
addMapping(
"cvc-pattern-valid: Value '(.*)' is not facet-valid with respect to pattern '(.*)' for type '(.*)'.",
"{2} is invalid. \"{0}\" should conform to the pattern - {1}",
NONE, NONE, HUMANIZE | CAPITALIZE | REMOVE_TYPE_SUFFIX) ;
addMapping(
"cvc-minLength-valid: Value '(.*)' with length = '0' is not facet-valid with respect to minLength '1' for type '#AnonType_commandexec'.",
"Command attribute cannot be blank in a command snippet.",
NONE, NONE) ;
addMapping(
"cvc-elt.1: Cannot find the declaration of element '(.*)'.",
"Invalid XML tag \"{0}\" found.",
NONE) ;
addMapping(
"cvc-[^:]+: (.*)",
"{0}");
}
private static void addMapping(String pattern, String replacement, int... transforms) {
errorMapping.add(new MappingEntry(pattern, replacement, transforms));
}
private static class MappingEntry {
public final Pattern pattern;
public final String replacement;
private final int[] transforms;
public MappingEntry(String pattern, String replacement, int[] transforms) {
this.transforms = transforms;
this.pattern = Pattern.compile(pattern);
this.replacement = replacement;
}
public String translate(String message) {
final Matcher matcher = pattern.matcher(message);
if (matcher.matches()) {
return MessageFormat.format(replacement, applyTransforms(extractArguments(matcher)));
}
return null;
}
private String[] extractArguments(Matcher matcher) {
String[] args = new String[matcher.groupCount()];
for (int i = 1; i <= matcher.groupCount(); i++) {
args[i - 1] = matcher.group(i);
}
return args;
}
private Object[] applyTransforms(String[] args) {
for (int i = 0, transformsLength = transforms.length; i < transformsLength; i++) {
int transform = transforms[i];
if ((transform & HUMANIZE) != 0) {
args[i] = StringUtil.humanize(args[i]);
}
if ((transform & CAPITALIZE) != 0) {
args[i] = StringUtils.capitalize(args[i]);
}
if ((transform & REMOVE_TYPE_SUFFIX) != 0) {
args[i] = StringUtils.replace(args[i], " type", "");
}
}
return args;
}
}
public void error(SAXParseException exception) throws SAXException {
if (!validationError) {
validationError = true;
saxParseException = exception;
}
}
public void fatalError(SAXParseException exception) throws SAXException {
if (!validationError) {
validationError = true;
saxParseException = exception;
}
}
public void warning(SAXParseException exception) throws SAXException {
}
public String translate() {
String msg = saxParseException.getMessage();
for (MappingEntry mappingEntry : errorMapping) {
String translated = mappingEntry.translate(msg);
if (translated != null) {
return translated;
}
}
return msg;
}
public boolean hasValidationError() {
return validationError;
}
}