/*
* gvNIX is an open source tool for rapid application development (RAD).
* Copyright (C) 2010 Generalitat Valenciana
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gvnix.service.roo.addon.addon.util;
import java.beans.Introspector;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.gvnix.service.roo.addon.addon.ws.WSConfigService.WsType;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
/**
* gvNIX Wsdl parser utilities.
* <p>
* Compatible address should be SOAP protocol version 1.1 and 1.2.
* </p>
*
* @author <a href="http://www.disid.com">DISID Corporation S.L.</a> made for <a
* href="http://www.dgti.gva.es">General Directorate for Information
* Technologies (DGTI)</a>
*/
public class WsdlParserUtils {
private static final String ROOT_ELEMENT_REQUIRED = "Wsdl root element required";
/** Compatible SOAP 1.1 and SOAP 1.2 namespaces **/
public static final String SOAP_11_NAMESPACE = "http://schemas.xmlsoap.org/wsdl/soap/";
public static final String NAMESPACE_WITHOUT_SLASH_11 = SOAP_11_NAMESPACE
.substring(0, SOAP_11_NAMESPACE.length() - 1);
public static final String SOAP_12_NAMESPACE = "http://schemas.xmlsoap.org/wsdl/soap12/";
public static final String NAMESPACE_WITHOUT_SLASH_12 = SOAP_12_NAMESPACE
.substring(0, SOAP_12_NAMESPACE.length() - 1);
public static final String XML_NAMESPACE_PREFIX = "xmlns:";
/** Character separators in different strings **/
public static final String NAMESPACE_SEPARATOR = ":";
public static final String FILE_SEPARATOR = File.separator;
public static final String XPATH_SEPARATOR = "/";
public static final String PACKAGE_SEPARATOR = ".";
/** Tokens in a namespace that are treated as package name part separators. */
protected static final char[] pkgSeparators = { '.', ':' };
/** Field javaPkgSeparator */
public static final char javaPkgSeparator = pkgSeparators[0];
/**
* These are java keywords as specified at the following URL (sorted
* alphabetically).
* http://java.sun.com/docs/books/jls/second_edition/html/lexical
* .doc.html#229308 Note that false, true, and null are not strictly
* keywords; they are literal values, but for the purposes of this array,
* they can be treated as literals. ****** PLEASE KEEP THIS LIST SORTED IN
* ASCENDING ORDER ******
*/
protected static final String keywords[] = { "abstract", "assert",
"boolean", "break", "byte", "case", "catch", "char", "class",
"const", "continue", "default", "do", "double", "else", "extends",
"false", "final", "finally", "float", "for", "goto", "if",
"implements", "import", "instanceof", "int", "interface", "long",
"native", "new", "null", "package", "private", "protected",
"public", "return", "short", "static", "strictfp", "super",
"switch", "synchronized", "this", "throw", "throws", "transient",
"true", "try", "void", "volatile", "while" };
/** Collator for comparing the strings */
public static final Collator englishCollator = Collator
.getInstance(Locale.ENGLISH);
/** Use this character as suffix */
public static final char keywordPrefix = '_';
/** Path to client generated sources (axis and cxf) **/
public static final String TARGET_GENERATED_SOURCES_PATH = "."
+ FILE_SEPARATOR + "target" + FILE_SEPARATOR + "generated-sources"
+ FILE_SEPARATOR + "client";
/** WSDL element names used **/
public static final String DEFINITIONS_ELEMENT = "definitions";
public static final String BINDING_ELEMENT = "binding";
public static final String PORT_TYPE_ELEMENT = "portType";
public static final String SERVICE_ELEMENT = "service";
public static final String PORT_ELEMENT = "port";
public static final String ADDRESS_ELEMENT = "address";
/** WSDL attribute names used **/
public static final String TARGET_NAMESPACE_ATTRIBUTE = "targetNamespace";
public static final String NAME_ATTRIBUTE = "name";
public static final String BINDING_ATTRIBUTE = "binding";
public static final String TYPE_ATTRIBUTE = "type";
public static final String STYLE_ATTRIBUTE = "style";
/** WSDL xpaths used **/
public static final String BINDINGS_XPATH = XPATH_SEPARATOR
+ DEFINITIONS_ELEMENT + XPATH_SEPARATOR + BINDING_ELEMENT;
public static final String PORT_TYPES_XPATH = XPATH_SEPARATOR
+ DEFINITIONS_ELEMENT + XPATH_SEPARATOR + PORT_TYPE_ELEMENT;
public static final String ADDRESSES_XPATH = XPATH_SEPARATOR
+ DEFINITIONS_ELEMENT + XPATH_SEPARATOR + SERVICE_ELEMENT
+ XPATH_SEPARATOR + PORT_ELEMENT + XPATH_SEPARATOR
+ ADDRESS_ELEMENT;
public static final String CHILD_BINDINGS_XPATH = XPATH_SEPARATOR
+ DEFINITIONS_ELEMENT + XPATH_SEPARATOR + BINDING_ELEMENT
+ XPATH_SEPARATOR + BINDING_ELEMENT;
/**
* Get the namespace attribute from root wsdl
*
* @param root Root element of the wsdl
* @return Wsdl namespace
*/
public static String getTargetNamespace(Element root) {
// Get the namespace attribute from root wsdl
String namespace = root.getAttribute(TARGET_NAMESPACE_ATTRIBUTE);
StringUtils.isNotBlank(namespace);
return namespace;
}
/**
* Constructs a valid java package path from target namespace of root wsdl.
* <p>
* Package ends with the package separator. Related package is different
* when web service is rpc encoded or not.
* </p>
*
* @param root Root element of the wsdl
* @return Equivalent java package or empty
*/
public static String getTargetNamespaceRelatedPackage(Element root) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
// Get the namespace attribute from root wsdl
String namespace = getTargetNamespace(root);
String pkg = getTargetNamespaceRelatedPackage(namespace, root)
.toLowerCase();
pkg = pkg.replace('_', 'u');
return pkg.concat(".");
}
/**
* Constructs a valid java package path from a namespace.
* <p>
* Package ends with the package separator. If target namespace has not a
* compatible prefix, empty string will be returned.
* </p>
*
* @param namespace Name space
* @param root Root element of the wsdl
* @return Equivalent java package or empty
*/
private static String getTargetNamespaceRelatedPackage(String namespace,
Element root) {
return normalizePackageName(makePackageName(namespace),
javaPkgSeparator);
}
/**
* Method normalizePackageName.
*
* @param pkg
* @param separator
* @return
*/
private static String normalizePackageName(String pkg, char separator) {
for (int i = 0; i < pkgSeparators.length; i++) {
pkg = pkg.replace(pkgSeparators[i], separator);
}
return pkg;
}
/**
* Method makePackageName.
*
* @param namespace
* @return
*/
public static String makePackageName(String namespace) {
String hostname = null;
String path = "";
// get the target namespace of the document
try {
URL u = new URL(namespace);
hostname = u.getHost();
path = u.getPath();
}
catch (MalformedURLException e) {
if (namespace.indexOf(':') > -1) {
hostname = namespace.substring(namespace.indexOf(':') + 1);
if (hostname.indexOf('/') > -1) {
hostname = hostname.substring(0, hostname.indexOf('/'));
}
}
else {
hostname = namespace;
}
}
// if we didn't file a hostname, bail
if (hostname == null) {
return null;
}
// convert illegal java identifier
hostname = hostname.replace('-', '_');
path = path.replace('-', '_');
// chomp off last forward slash in path, if necessary
if ((path.length() > 0) && (path.charAt(path.length() - 1) == '/')) {
path = path.substring(0, path.length() - 1);
}
// tokenize the hostname and reverse it
StringTokenizer st = new StringTokenizer(hostname, ".:");
String[] words = new String[st.countTokens()];
for (int i = 0; i < words.length; ++i) {
words[i] = st.nextToken();
}
StringBuffer sb = new StringBuffer(namespace.length());
for (int i = words.length - 1; i >= 0; --i) {
addWordToPackageBuffer(sb, words[i], (i == words.length - 1));
}
// tokenize the path
StringTokenizer st2 = new StringTokenizer(path, "/");
while (st2.hasMoreTokens()) {
addWordToPackageBuffer(sb, st2.nextToken(), false);
}
return sb.toString();
}
/**
* Massage word into a form suitable for use in a Java package name.
* <p>
* Append it to the target string buffer with a <tt>.</tt> delimiter if
* <tt>word</tt> is not the first word in the package name.
* </p>
*
* @param sb the buffer to append to
* @param word the word to append
* @param firstWord a flag indicating whether this is the first word
*/
private static void addWordToPackageBuffer(StringBuffer sb, String word,
boolean firstWord) {
if (isJavaKeyword(word)) {
word = makeNonJavaKeyword(word);
}
// separate with dot after the first word
if (!firstWord) {
sb.append('.');
}
// prefix digits with underscores
if (Character.isDigit(word.charAt(0))) {
sb.append('_');
}
// replace periods with underscores
if (word.indexOf('.') != -1) {
char[] buf = word.toCharArray();
for (int i = 0; i < word.length(); i++) {
if (buf[i] == '.') {
buf[i] = '_';
}
}
word = new String(buf);
}
sb.append(word);
}
/**
* Checks if the input string is a valid java keyword.
*
* @return boolean true/false
*/
public static boolean isJavaKeyword(String keyword) {
return (Arrays.binarySearch(keywords, keyword, englishCollator) >= 0);
}
/**
* Turn a java keyword string into a non-Java keyword string.
* <p>
* Right now this simply means appending an underscore.
* </p>
*/
public static String makeNonJavaKeyword(String keyword) {
return keywordPrefix + keyword;
}
/**
* Find the first compatible address element of the root.
* <p>
* Compatible address should be SOAP protocol version 1.1 and 1.2.
* </p>
*
* @param root Root element of wsdl.
* @return First compatible address element or null if no element.
*/
public static Element findFirstCompatibleAddress(Element root) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
// Find all address elements
List<Element> addresses = XmlUtils.findElements(ADDRESSES_XPATH, root);
// Separate on a list the addresses prefix
List<String> prefixes = new ArrayList<String>();
for (int i = 0; i < addresses.size(); i++) {
String nodeName = addresses.get(i).getNodeName();
prefixes.add(i, getNamespace(nodeName));
}
// Separate on a list the addresses namespace
List<String> namespaces = new ArrayList<String>();
for (int i = 0; i < prefixes.size(); i++) {
namespaces.add(i, getNamespaceURI(root, prefixes.get(i)));
}
// Any namepace is a SOAP namespace with or whitout final slash ?
int index;
boolean soap1_2 = false;
boolean soap1_1 = false;
if ((index = namespaces.indexOf(SOAP_12_NAMESPACE)) != -1
|| (index = namespaces.indexOf(NAMESPACE_WITHOUT_SLASH_12)) != -1) {
// First preference: SOAP 1.2 protocol
soap1_2 = true;
}
else if ((index = namespaces.indexOf(SOAP_11_NAMESPACE)) != -1
|| (index = namespaces.indexOf(NAMESPACE_WITHOUT_SLASH_11)) != -1) {
// Second preference: SOAP 1.1 protocol
soap1_1 = true;
}
if (!(soap1_2 || soap1_1)) {
// Other protocols not supported
return null;
}
return addresses.get(index);
}
/**
* Obtain from the list the element with the reference on the root wsdl.
*
* @param root Root wsdl
* @param elements Elements list to search in
* @param reference Reference to be searched
* @return Element found or null if not
*/
private static Element getReferencedElement(Element root,
List<Element> elements, String reference) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
Validate.notNull(elements, "Elements list required");
Validate.notNull(reference, "Reference required");
String prefix = getNamespace(reference);
String sufix = getLocalName(reference);
String namespace = getNamespaceURI(root, prefix);
Element element = null;
for (Element elementIter : elements) {
String referenceIter = elementIter.getAttribute(NAME_ATTRIBUTE);
String prefixIter = getNamespace(referenceIter);
String sufixIter = getLocalName(referenceIter);
String namespaceIter = getNamespaceURI(root, prefixIter);
if (sufixIter.equals(sufix) && namespaceIter.equals(namespace)) {
element = elementIter;
}
}
return element;
}
/**
* Get the path to the generated service class.
*
* @param root Wsdl root element
* @param sense Communication sense type
* @return Path to the class
*/
public static String getServiceClassPath(Element root, WsType sense) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
// Build the classpath related to the namespace
String path = getTargetNamespaceRelatedPackage(root);
// Find a compatible service name
String name = findFirstCompatibleServiceClassName(root, sense);
if (sense.equals(WsType.IMPORT_RPC_ENCODED)) {
// Rpc generated service source ends with this string
name = name.concat("Locator");
}
// Class path is the concat of path and name
return path + capitalizeFirstChar(name);
}
/**
* Get the path to the generated port type class.
*
* @param root Wsdl root element
* @param sense Communication sense type
* @return Path to the class
*/
public static String getPortTypeClassPath(Element root, WsType sense) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
// Build the classpath related to the namespace
String path = getTargetNamespaceRelatedPackage(root);
// Find a compatible port and port type name
String portType = findFirstCompatiblePortTypeClassName(root, sense);
String port = findFirstCompatiblePortClassName(root, sense);
// RPC Encoded web services adds sufix to port type when equals to port
if (WsType.IMPORT_RPC_ENCODED.equals(sense) && portType.equals(port)) {
portType = portType.concat("_PortType");
}
// Class path is the concat of path and name
return path + capitalizeFirstChar(portType);
}
/**
* Get the port type Java file
*
* @param root Wsdl root element
* @param sense Communication sense type
* @return Java file
*/
public static File getPortTypeJavaFile(Element root, WsType sense) {
return getGeneratedJavaFile(convertTypePathToJavaPath(getPortTypeClassPath(
root, sense)));
}
/**
* Convert the path to a type to the java file path.
*
* @param classPath Path to the class
* @return Path to the java file
*/
private static String convertTypePathToJavaPath(String classPath) {
StringUtils.isNotBlank(classPath);
return classPath.replace(PACKAGE_SEPARATOR, FILE_SEPARATOR).concat(
".java");
}
/**
* Get the file on path in generated sources folder.
*
* @param path Searched path
* @return File to path
*/
private static File getGeneratedJavaFile(String path) {
StringUtils.isNotBlank(path);
return new File(TARGET_GENERATED_SOURCES_PATH, path);
}
/**
* Find the first compatible service related class name of the root.
* <p>
* Compatible service should be SOAP protocol version 1.1 and 1.2.
* </p>
*
* @param root Root element of wsdl
* @param sense Communication sense type
* @return First compatible service class name
*/
private static String findFirstCompatibleServiceClassName(Element root,
WsType sense) {
String name = findFirstCompatibleServiceElementName(root);
return convertNameToJavaFormat(name, sense);
}
/**
* Find the first compatible service related element name of the root.
* <p>
* Compatible service should be SOAP protocol version 1.1 and 1.2.
* </p>
*
* @param root Root element of wsdl
* @param sense Communication sense type
* @return First compatible service class name
*/
public static String findFirstCompatibleServiceElementName(Element root) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
Element port = findFirstCompatiblePort(root);
// Get the path to the service class defined by the wsdl
Element service = ((Element) port.getParentNode());
String name = service.getAttribute(NAME_ATTRIBUTE);
StringUtils.isNotBlank(name);
return name;
}
/**
* Find the first compatible port element of the root.
* <p>
* Compatible port should be SOAP protocol version 1.1 and 1.2.
* </p>
*
* @param root Root element of wsdl
* @return First compatible port element
*/
public static Element findFirstCompatiblePort(Element root) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
// Find a compatible address element
Element address = findFirstCompatibleAddress(root);
Validate.notNull(address, "No compatible SOAP 1.1 or 1.2 protocol");
// Get the port element defined by the wsdl
Element port = ((Element) address.getParentNode());
return port;
}
/**
* Check port if Supported port element of the root.
* <p>
* Should exists only one compatible port using SOAP protocol version 1.1 or
* 1.2.
* </p>
*
* @param root Root element of wsdl
* @return Compatible port element
*/
public static Element checkCompatiblePort(Element root) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
// Find a compatible address element
Element address = checkCompatibleAddress(root);
Validate.notNull(address, "No compatible SOAP 1.1 or 1.2 protocol");
// Get the port element defined by the wsdl
Element port = ((Element) address.getParentNode());
return port;
}
/**
* Check compatible address element of the root.
* <p>
* Should exists only one compatible address using SOAP protocol version 1.1
* or 1.2.
* </p>
*
* @param root Root element of wsdl.
* @return First compatible address element or null if no element.
*/
public static Element checkCompatibleAddress(Element root) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
// Find all address elements
List<Element> addresses = XmlUtils.findElements(ADDRESSES_XPATH, root);
// Separate on a list the addresses prefix
List<String> prefixes = new ArrayList<String>();
for (int i = 0; i < addresses.size(); i++) {
String nodeName = addresses.get(i).getNodeName();
prefixes.add(i, getNamespace(nodeName));
}
// Separate on a list the addresses namespace
List<String> namespaces = new ArrayList<String>();
for (int i = 0; i < prefixes.size(); i++) {
namespaces.add(i, getNamespaceURI(root, prefixes.get(i)));
}
// Any namepace is a SOAP namespace with or whitout final slash ?
boolean isSoap12Compatible = false;
boolean isSoap11Compatible = false;
int indexSoap12 = 0;
int indexSoap11 = 0;
if ((indexSoap12 = namespaces.indexOf(SOAP_12_NAMESPACE)) != -1
|| (indexSoap12 = namespaces
.indexOf(NAMESPACE_WITHOUT_SLASH_12)) != -1) {
// First preference: SOAP 1.2 protocol
isSoap12Compatible = true;
}
if ((indexSoap11 = namespaces.indexOf(SOAP_11_NAMESPACE)) != -1
|| (indexSoap11 = namespaces
.indexOf(NAMESPACE_WITHOUT_SLASH_11)) != -1) {
if (isSoap12Compatible) {
Validate.validState(
false,
"There are defined SOAP 1.1 and 1.2 protocols.\nMust be only one protocol defined.");
}
isSoap11Compatible = true;
// Second preference: SOAP 1.1 protocol
}
int index = 0;
if (isSoap12Compatible && !isSoap11Compatible) {
index = indexSoap12;
}
else if (isSoap11Compatible && !isSoap12Compatible) {
index = indexSoap11;
}
else {
// Other protocols not supported
return null;
}
return addresses.get(index);
}
/**
* Find the first compatible port related class name of the root.
* <p>
* Compatible port should be SOAP protocol version 1.1 and 1.2.
* </p>
*
* @param root Root element of wsdl
* @param sense Communication sense type
* @return First compatible port element class name
*/
public static String findFirstCompatiblePortClassName(Element root,
WsType sense) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
// Get the the port element name
return convertNameToJavaFormat(findFirstCompatiblePort(root)
.getAttribute(NAME_ATTRIBUTE), sense);
}
/**
* Find the first compatible port type class name of the root.
* <p>
* Compatible port type should be SOAP protocol version 1.1 and 1.2.
* </p>
*
* @param root Root element of wsdl
* @param sense Communication sense type
* @return First compatible port type class name
*/
private static String findFirstCompatiblePortTypeClassName(Element root,
WsType sense) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
Element binding = findFirstCompatibleBinding(root);
// Find all port types elements
List<Element> portTypes = XmlUtils.findElements(PORT_TYPES_XPATH, root);
Validate.notEmpty(portTypes, "No valid port type format");
String portTypeRef = binding.getAttribute(TYPE_ATTRIBUTE);
StringUtils.isNotEmpty(portTypeRef);
Element portType = getReferencedElement(root, portTypes, portTypeRef);
Validate.notNull(portType, "No valid port type reference");
String portTypeName = portType.getAttribute(NAME_ATTRIBUTE);
StringUtils.isNotEmpty(portTypeName);
return convertNameToJavaFormat(portTypeName, sense);
}
/**
* Find the first compatible binding name of the root.
* <p>
* Compatible binding should be SOAP protocol version 1.1 and 1.2.
* </p>
*
* @param root Root element of wsdl
* @return First compatible binding element
*/
private static Element findFirstCompatibleBinding(Element root) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
// Find all binding elements
List<Element> bindings = XmlUtils.findElements(BINDINGS_XPATH, root);
Validate.notEmpty(bindings, "No valid binding format");
Element port = findFirstCompatiblePort(root);
String bindingRef = port.getAttribute(BINDING_ATTRIBUTE);
StringUtils.isNotEmpty(bindingRef);
Element binding = getReferencedElement(root, bindings, bindingRef);
Validate.notNull(binding, "No valid binding reference");
return binding;
}
/**
* URI of a wsdl namespace, or target namespace if not exists or null.
* <p>
* URI is defined by the value of the root attributes that starts with
* 'xmlns' namespace. For example, for element
* 'xmlns:tns="http://tempuri.org/"' the uri is 'http://tempuri.org/'.
* </p>
*
* @param root Wsdl root element
* @param namespace Namespace to search it URI
* @return Namespace URI related to the namespace
*/
private static String getNamespaceURI(Element root, String namespace) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
String namespaceURI = null;
if (namespace != null && namespace.length() > 0) {
// Get the namespace related to the prefix
namespaceURI = root.getAttribute(XML_NAMESPACE_PREFIX + namespace);
}
if (namespaceURI == null) {
namespaceURI = getTargetNamespace(root);
}
return namespaceURI;
}
/**
* Get the prefix of a name, or empty if not.
* <p>
* Prefix is the text before first namespace separator character. For
* example, for name '<soap12:address>' the prefix is 'soap12'.
* </p>
*
* @param elementName An element name
* @return Prefix of the name or empty if not
*/
protected static String getNamespace(String elementName) {
Validate.notNull(elementName, "Element name required");
String prefix = "";
// Get the index of the namespace separator char
int index = elementName.indexOf(NAMESPACE_SEPARATOR);
if (index != -1) {
// Get the prefix
prefix = elementName.substring(0, index);
}
return prefix;
}
/**
* Get the local name of an element.
* <p>
* Local name is the text after first namespace separator character. For
* example, for element name soap12:address the local name is 'address'.
* </p>
*
* @param elementName An element name
* @return Sufix of the name or name if not
*/
protected static String getLocalName(String elementName) {
Validate.notNull(elementName, "Element name required");
return elementName.replaceFirst(getNamespace(elementName)
+ NAMESPACE_SEPARATOR, "");
}
/**
* Converts a wsdl name to a valid Java format.
* <p>
* The conversion is different if RPC/Encoded communication sense.
* </p>
*
* @param name A wsdl name
* @param sense Communication sense type
* @return Valid java name
*/
private static String convertNameToJavaFormat(String name, WsType sense) {
if (WsType.IMPORT_RPC_ENCODED.equals(sense)) {
return convertRpcNameToJavaFormat(name);
}
return convertDocumentNameToJavaFormat(name);
}
/**
* Converts a wsdl name to a valid Java format in Document.
* <p>
* Valid chars are letters, numbers and $. '-', '_', ':' and '.' chars are
* replaced with none. Other chars are replaced by unicode value with format
* "_002f". New words in name always will be start by uppercase.
* </p>
*
* @param name A wsdl name
* @return Valid java name
*/
private static String convertDocumentNameToJavaFormat(String name) {
Validate.notNull(name, "Name required");
StringBuffer ostr = new StringBuffer();
// First character, uppercase
boolean upper = true;
for (int i = 0; i < name.length(); i++) {
char ch = name.charAt(i);
// Letter, number or $
if ((ch >= 'a') && (ch <= 'z') || (ch >= 'A') && (ch <= 'Z')
|| (ch >= '0') && (ch <= '9') || ch == '$') {
if (upper) {
ostr.append(Character.toUpperCase(ch));
}
else {
ostr.append(ch);
}
if ((ch >= '0') && (ch <= '9') || ch == '$') {
// Next character to number or $ will be uppercase
upper = true;
}
else {
upper = false;
}
}
else {
// Next characters will be replace by none, others to Unicode
if (ch != '-' && ch != '_' && ch != ':' && ch != '.') {
// Unicode prefix
ostr.append("_");
// Unicode value
String hex = Integer.toHexString(name.charAt(i) & 0xFFFF);
for (int j = 0; j < 4 - hex.length(); j++) {
// Prepend zeros because unicode requires 4 digits
ostr.append("0");
}
// Standard unicode format
ostr.append(hex.toLowerCase());
}
// Next character will be uppercase
upper = true;
}
}
return (new String(ostr));
}
/**
* Converts a wsdl name to a valid Java format in RPC.
*
* @param name A wsdl name
* @return Valid java name
*/
private static String convertRpcNameToJavaFormat(String name) {
String java = new String(name);
if (!isJavaId(java)) {
java = xmlNameToJavaClass(name);
}
return java;
}
/**
* Returns true if the name is a valid java identifier.
*
* @param id to check
* @return boolean true/false
**/
public static boolean isJavaId(String id) {
if (id == null || id.equals("") || isJavaKeyword(id))
return false;
if (!Character.isJavaIdentifierStart(id.charAt(0)))
return false;
for (int i = 1; i < id.length(); i++)
if (!Character.isJavaIdentifierPart(id.charAt(i)))
return false;
return true;
}
/**
* Map an XML name to a valid Java identifier w/ capitalized first letter.
*
* @param name
* @return
*/
public static String xmlNameToJavaClass(String name) {
return capitalizeFirstChar(xmlNameToJava(name));
}
/**
* Capitalize the first character of the name.
*
* @param name
* @return
*/
public static String capitalizeFirstChar(String name) {
if ((name == null) || name.equals("")) {
return name;
}
char start = name.charAt(0);
if (Character.isLowerCase(start)) {
start = Character.toUpperCase(start);
return start + name.substring(1);
}
return name;
}
/**
* Map an XML name to a Java identifier per the mapping rules of JSR 101 (in
* version 1.0 this is "Chapter 20: Appendix: Mapping of XML Names"
*
* @param name is the xml name
* @return the java name per JSR 101 specification
*/
public static String xmlNameToJava(String name) {
// protect ourselves from garbage
if (name == null || name.equals(""))
return name;
char[] nameArray = name.toCharArray();
int nameLen = name.length();
StringBuffer result = new StringBuffer(nameLen);
boolean wordStart = false;
// The mapping indicates to convert first character.
int i = 0;
while (i < nameLen
&& (isPunctuation(nameArray[i]) || !Character
.isJavaIdentifierStart(nameArray[i]))) {
i++;
}
if (i < nameLen) {
// Decapitalization code used to be here, but we use the
// Introspector function now after we filter out all bad chars.
result.append(nameArray[i]);
// wordStart = !Character.isLetter(nameArray[i]);
wordStart = !Character.isLetter(nameArray[i])
&& nameArray[i] != "_".charAt(0);
}
else {
// The identifier cannot be mapped strictly according to
// JSR 101
if (Character.isJavaIdentifierPart(nameArray[0])) {
result.append("_" + nameArray[0]);
}
else {
// The XML identifier does not contain any characters
// we can map to Java. Using the length of the string
// will make it somewhat unique.
result.append("_" + nameArray.length);
}
}
// The mapping indicates to skip over
// all characters that are not letters or
// digits. The first letter/digit
// following a skipped character is
// upper-cased.
for (++i; i < nameLen; ++i) {
char c = nameArray[i];
// if this is a bad char, skip it and remember to capitalize next
// good character we encounter
if (isPunctuation(c) || !Character.isJavaIdentifierPart(c)) {
wordStart = true;
continue;
}
if (wordStart && Character.isLowerCase(c)) {
result.append(Character.toUpperCase(c));
}
else {
result.append(c);
}
// If c is not a character, but is a legal Java
// identifier character, capitalize the next character.
// For example: "22hi" becomes "22Hi"
// wordStart = !Character.isLetter(c);
wordStart = !Character.isLetter(c) && c != "_".charAt(0);
}
// covert back to a String
String newName = result.toString();
// Follow JavaBean rules, but we need to check if the first
// letter is uppercase first
if (Character.isUpperCase(newName.charAt(0)))
newName = Introspector.decapitalize(newName);
// check for Java keywords
if (isJavaKeyword(newName))
newName = makeNonJavaKeyword(newName);
return newName;
}
/**
* Is this an XML punctuation character?
*/
private static boolean isPunctuation(char c) {
return '-' == c || '.' == c || ':' == c || '\u00B7' == c
|| '\u0387' == c || '\u06DD' == c || '\u06DE' == c;
}
/**
* Is wsdl document root element rpc encoded ?
*
* @param root Wsdl document root element
* @return is rpc endoded
*/
public static boolean isRpcEncoded(Element root) {
Validate.notNull(root, ROOT_ELEMENT_REQUIRED);
// Find binding element
Element binding = findFirstCompatibleBinding(root);
// Find all child bindings
List<Element> childs = XmlUtils
.findElements(CHILD_BINDINGS_XPATH, root);
Validate.notEmpty(childs, "No valid child bindings format");
// Get child binding related to binding element
for (Element child : childs) {
// Get child parent binding element name
Element parentBinding = ((Element) child.getParentNode());
String name = parentBinding.getAttribute(NAME_ATTRIBUTE);
StringUtils.isNotEmpty(name);
// If parent binding has the same name as binding
if (name.equals(binding.getAttribute(NAME_ATTRIBUTE))) {
// Check RPC style
String style = child.getAttribute(STYLE_ATTRIBUTE);
StringUtils.isNotEmpty(name);
if ("rpc".equalsIgnoreCase(style)) {
return true;
}
}
/*
* TODO To be completed like next condition for each operation:
*
* (bindingStyle = RPC | operationStyle == RPC) & (inputUse =
* ENCODED | outputUse = ENCODED)
*
* If any operation match previous condition, then is rpc encoded
*/
}
return false;
}
/**
* Check connection and WSDL format from the given url.<br/>
* If you need SSL support acceding to WSLD you should use
* {@link SecurityService#getWsdl(String)}
*
* @param url URL to check
* @return Wsdl document root element
* @exception IllegalStateException wsdl no connection or invalid
*/
public static Element validateWsdlUrl(String url) {
try {
// Parse the wsdl location to a DOM document
Document wsdl = XmlUtils.getDocumentBuilder().parse(url);
Element root = wsdl.getDocumentElement();
Validate.notNull(root, "No valid document format");
return root;
}
catch (SAXException e) {
throw new IllegalStateException("The format of the wsdl has errors");
}
catch (IOException e) {
throw new IllegalStateException("There is no access to the wsdl");
}
}
}