diff options
author | lmandel | 2005-11-07 07:29:37 +0000 |
---|---|---|
committer | lmandel | 2005-11-07 07:29:37 +0000 |
commit | d702c81d0ff6daf1bd3ccfb9760a83331106008e (patch) | |
tree | d09e5ca6da53b201ca75ca3b1cc8e9d1210cfd00 /bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11 | |
parent | 451e5d5e7454e9c4befcd395dace52aae74d46cc (diff) | |
download | webtools.webservices-d702c81d0ff6daf1bd3ccfb9760a83331106008e.tar.gz webtools.webservices-d702c81d0ff6daf1bd3ccfb9760a83331106008e.tar.xz webtools.webservices-d702c81d0ff6daf1bd3ccfb9760a83331106008e.zip |
[99731] Separated WSDL validation core and ui.
Diffstat (limited to 'bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11')
24 files changed, 7438 insertions, 0 deletions
diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/IWSDL11ValidationInfo.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/IWSDL11ValidationInfo.java new file mode 100644 index 000000000..0c1bc4ff1 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/IWSDL11ValidationInfo.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +import java.util.Hashtable; + +import org.apache.xerces.xs.XSModel; +import org.eclipse.wst.wsdl.validation.internal.resolver.URIResolver; + +/** + * An interface for WSDL 1.1 validation information. Uses an existing + * validation info object and provides methods to set and retrieve + * schemas and convenience methods for setting errors with objects. + */ +public interface IWSDL11ValidationInfo +{ + /** + * Returns the URI of the file being validated. + * + * @return The URI of the file being validated. + */ + public String getFileURI(); + + /** + * Add a schema to the list of schemas available for this WSDL document. + * + * @param xsModel The schema to add to the list. + */ + public void addSchema(XSModel xsModel); + + /** + * Get an array of all the schemas available for this WSDL document. + * + * @return An array of all the schemas available for this WSDL document. + */ + public XSModel[] getSchemas(); + + /** + * Clear all the stored schemas. + */ + public void clearSchemas(); + + /** + * Set the element locations hashtable. + * + * @param elementLocations The hashtable to set with the element locations. + */ + public void setElementLocations(Hashtable elementLocations); + + /** + * Convenience method for extensibly validators to add error messages. + * + * @param message The error to add. + * @param element The object to add the error for. + */ + public void addError(String message, Object element); + + /** + * Convenience method for extensibly validators to add error messages. + * + * @param message The error to add. + * @param element The object to add the error for. + * @param errorKey The error key for this message + * @param messageArguments The strings used to create the message. + */ + public void addError(String message, Object element, String errorKey, Object[] messageArguments); + + /** + * Add an error message at the given line and column. + * + * @param message The error to add. + * @param line The line location of the error. + * @param column The column location of the error. + * @param uri The uri of the file containing the error. + */ + public void addError(String message, int line, int column, String uri); + + /** + * Convenience method for extensibly validators to add warning messages. + * + * @param message The warning to add. + * @param element The object to add the warning for. + */ + public void addWarning(String message, Object element); + + /** + * Add a warning message at the given line and column. + * + * @param message The warning to add. + * @param line The line location of the warning. + * @param column The column location of the warning. + * @param uri The uri of the file containing the warning. + */ + public void addWarning(String message, int line, int column, String uri); + + /** + * Get the URI resolver in use for this validation. The URI resolver + * returned is actually a URI resolver handler that will + * iterate through all of the registered URI resolvers. + * + * @return The URI resolver handler. + */ + public URIResolver getURIResolver(); +} + diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/IWSDL11Validator.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/IWSDL11Validator.java new file mode 100644 index 000000000..a6bd2e23d --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/IWSDL11Validator.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +import java.util.List; +import java.util.ResourceBundle; + +/** + * Interface for a validator plugged into the WSDL 1.1 validator. + */ +public interface IWSDL11Validator +{ + /** + * Validate the given element. + * + * @param element The element to validate. + * @param parents A list of parents of this element. + * @param valInfo The current validation information. + */ + public void validate(Object element, List parents, IWSDL11ValidationInfo valInfo); + + /** + * Set the resource bundle of the validator. + * + * @param rb The resource bundle to set. + */ + public void setResourceBundle(ResourceBundle rb); + +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/ImportHolder.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/ImportHolder.java new file mode 100644 index 000000000..205bc9b79 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/ImportHolder.java @@ -0,0 +1,602 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.wsdl.Definition; +import javax.wsdl.Import; +import javax.wsdl.WSDLException; + +import org.apache.xerces.xs.XSModel; +import org.eclipse.wst.wsdl.validation.internal.IValidationMessage; +import org.eclipse.wst.wsdl.validation.internal.resolver.IURIResolutionResult; +import org.eclipse.wst.wsdl.validation.internal.util.ErrorMessage; +import org.eclipse.wst.wsdl.validation.internal.util.MessageGenerator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd.XSDValidator; +import org.eclipse.wst.wsdl.validation.internal.xml.AbstractXMLConformanceFactory; +import org.eclipse.wst.wsdl.validation.internal.xml.IXMLValidator; +import org.eclipse.wst.wsdl.validation.internal.xml.XMLCatalogResolver; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.InputSource; + +import com.ibm.wsdl.Constants; +import com.ibm.wsdl.util.StringUtils; +import com.ibm.wsdl.util.xml.DOMUtils; +import com.ibm.wsdl.util.xml.QNameUtils; + +/** + * A class to hold and parse an import element. + */ +public class ImportHolder implements Comparable +{ + private MessageGenerator messagegenerator; + + private WSDLDocument importingWSDLDoc = null; + private WSDLDocument wsdlDocument = null; + private Definition importingDef = null; + private Element importingDocImportElement = null; + private String namespace = null; + private String location = null; + private String classpathURI = null; + private String contextURI = null; + private int depth; + private Element element = null; + private List schemas = new ArrayList(); + private boolean isWSDLFileImport = true; + private boolean importInvalid = false; + private Import importDef = null; + private IWSDL11ValidationInfo valinfo; + + /** + * Constructor. + * + * @param namespace The namespace of the import. + * @param location The location of the import. + * @param contextURI The context URI for resolving the import location. + * @param wsdlDoc The WSDLDocument that contains the import. + * @param depth The depth of the import. + * @param importingDocImportElement The element representing the import in the encapsulating WSDLDocument. + * @param messagegenerator A messagegenerator for obtaining strings. + * @param valinfo The WSDL11ValidationInfo for reporting messages. + */ + public ImportHolder(String namespace, String location, String contextURI, WSDLDocument importingWSDLDoc, int depth, Element importingDocImportElement, MessageGenerator messagegenerator, IWSDL11ValidationInfo valinfo) + { + this.messagegenerator = messagegenerator; + this.valinfo = valinfo; + this.importingWSDLDoc = importingWSDLDoc; + if(importingWSDLDoc != null) + { + this.importingDef = importingWSDLDoc.getDefinition(); + } + this.importingDocImportElement = importingDocImportElement; + this.depth = depth; + this.namespace = namespace; + this.location = location; + + // Allow WSDL imports to have no location attribute even though it is required. + // Schema will normally catch the problem but this allows users to override the + // schema and have the validator run. + if (this.location == null) + { + this.location = namespace; + } + this.contextURI = contextURI; + + this.location = this.location.replace('\\','/'); + IURIResolutionResult classpathURI = valinfo.getURIResolver().resolve(this.contextURI, this.namespace, this.location); + if(classpathURI.getLogicalLocation() != null) + { + this.location = classpathURI.getLogicalLocation(); + } + if(classpathURI.getPhysicalLocation() != null) + { + this.classpathURI = classpathURI.getPhysicalLocation(); + this.contextURI = null; + } + } + + public void initialize() + { + Element documentElement = null; + try + { + documentElement = getElement(); + } + catch(WSDLException e) + { + } + if(documentElement != null) + { + // Handle WSDL imports. + if (QNameUtils.matches(Constants.Q_ELEM_DEFINITIONS, documentElement)) + { + if(isXMLValid(this.classpathURI)) + { + try + { + wsdlDocument = new WSDLDocument(this.location, documentElement, this.depth, this.messagegenerator, this.valinfo); + createWSDLImport(wsdlDocument); + } + catch(WSDLException e) + { + valinfo.addError(messagegenerator.getString("_UNABLE_TO_IMPORT_BAD_LOCATION", "'" + importDef.getLocationURI() + "'"), importingDocImportElement); + } + } + } + // Handle schema imports. + else if (QNameUtils.matches(Constants.Q_ELEM_XSD_2001, documentElement)) + { + createXSDImport(); + } + } + } + + protected boolean isXMLValid(String uri) + { + IXMLValidator xmlValidator = AbstractXMLConformanceFactory.getInstance().getXMLValidator(); + xmlValidator.setFile(uri); + //xmlValidator.setValidationInfo(valInfo); + xmlValidator.run(); + // if there are no xml conformance problems go on to check the wsdl stuff + if (xmlValidator.hasErrors()) + { + // temp handling of XML errors until validator is updated. + List errors = xmlValidator.getErrors(); + Iterator errorsIter = errors.iterator(); + while (errorsIter.hasNext()) + { + IValidationMessage valMes = (IValidationMessage)errorsIter.next(); + valinfo.addError(valMes.getMessage(), valMes.getLine(), valMes.getColumn(), valMes.getURI()); + } + importInvalid = true; + return false; + } + return true; + } + + /** + * Get the importing WSDLDocument. + * + * @return The importing WSDLDocument. + */ + public WSDLDocument getImportingDocument() + { + return importingWSDLDoc; + } + + /** + * Get the WSDL document this import represents. + * + * @return The WSDL document this import represents. + */ + public WSDLDocument getWSDLDocument() + { + return wsdlDocument; + } + + /** + * Get the namespace. + * + * @return The namespace. + */ + public String getNamespace() + { + return namespace; + } + + /** + * Get the location. + * + * @return The location. + */ + public String getLocation() + { + return location; + } + + /** + * Get the context URI. + * + * @return The context URI. + */ + public String getContextURI() + { + return contextURI; + } + + /** + * Get the depth in the WSDL tree. + * + * @return The depth in the WSDL tree. + */ + public int getDepth() + { + return depth; + } + + /** + * Get the containing defintions element. + * + * @return The containing definitions element. + */ + public Definition getImportingDefinition() + { + return importingDef; + } + + /** + * Get the element for this import. + * + * @return The element for this import. + * @throws WSDLException + */ + public Element getElement() throws WSDLException + { + if(element != null) + { + return element; + } + + String locationURI = location; + String namespaceURI = namespace; + //Import importDef = importingDef.createImport(); + + // Allow locating WSDL documents using any registered URI resolver. + //String classpathURI = URIResolver.getInstance().resolve(contextURI, namespaceURI, locationURI); +// if (!classpathURI.equals(locationURI)) +// { +// locationURI = classpathURI; +// contextURI = null; +// } + Reader reader = null; + if (locationURI != null) + { + try + { + //String contextURI = def.getDocumentBaseURI(); + //Definition importedDef = null; + + InputSource inputSource = null; + URL url = null; + + + URL contextURL = (contextURI != null) ? StringUtils.getURL(null, contextURI) : null; + + url = StringUtils.getURL(contextURL, locationURI); + + // Handle file:// urls. The correct format should be file:/// or file:/. + String urlAuthority = url.getAuthority(); + String urlProtocol = url.getProtocol(); + if(urlAuthority !=null && urlProtocol.equalsIgnoreCase("file") && !urlAuthority.equals("")) + { + url = new URL(urlProtocol,"","/" + urlAuthority + url.getFile()); + } + + String urlString = url.toString(); + // Test for case sensitivity on local files. + if(urlString.startsWith("file:")) + { + File testfile = new File(url.getFile()); + String testfileString = testfile.getAbsolutePath(); + String canonicalfileString = testfile.getCanonicalPath(); + + if (!testfileString.equals(canonicalfileString)) + { + if (!String.valueOf(testfileString.charAt(0)).equalsIgnoreCase + (String.valueOf(canonicalfileString.charAt(0))) + || !testfileString.substring(1,testfileString.length()).equals + (canonicalfileString.substring(1,canonicalfileString.length()))) + { + urlString = ""; + url = null; + } + } + } + if(url != null) + { + try + { + reader = StringUtils.getContentAsReader(url); + } + catch(IOException e) + { + // No need to do anything here. The error will be handled below. + } + } + if (reader != null) + { + inputSource = new InputSource(reader); + if(classpathURI != null && !classpathURI.equals(location)) + { + inputSource.setByteStream(new URL(classpathURI).openStream()); + } + } + + if (inputSource == null) + { + // Get the actual location from the element. + String actualLocation = DOMUtils.getAttribute(importingDocImportElement, Constants.ATTR_LOCATION); + if(actualLocation == null) + { + actualLocation = DOMUtils.getAttribute(importingDocImportElement, "schemaLocation"); + } + if(actualLocation == null) + { + actualLocation = namespace; + } + importingWSDLDoc.addReaderWarning( + importingDef, + importingDocImportElement, + messagegenerator.getString("_UNABLE_TO_IMPORT_BAD_LOCATION", "'" + actualLocation + "'")); + importInvalid = true; + + // TODO: modify the reader error to show in all the correct locations. + throw new WSDLException( + WSDLException.OTHER_ERROR, + "Unable to locate imported document " + + "at '" + + locationURI + + "'" + + (contextURI == null ? "." : ", relative to '" + contextURI + "'.")); + } + Document doc = null; + try + { + doc = WSDLReaderImpl.getDocument(inputSource, locationURI); + } + catch(WSDLException e) + { + // The File is invalid and cannot be read. + // Perform XML validation. + isXMLValid(locationURI); +// importingWSDLDoc.addReaderError( +// importingDef, +// importingDocImportElement, +// messagegenerator.getString("_UNABLE_TO_IMPORT_INVALID", "'" + location + "'")); + throw e; + } + element = doc.getDocumentElement(); + if(!QNameUtils.matches(Constants.Q_ELEM_DEFINITIONS, element)) + { + isWSDLFileImport = false; + } + // Ensure that the imported document has the same namespace as the import element states. + String importTargetNS = element.getAttribute(Constants.ATTR_TARGET_NAMESPACE); + if(!importTargetNS.equals(namespace)) + { + importingWSDLDoc.addReaderWarning( + importingDef, + importingDocImportElement, + messagegenerator.getString("_WARN_WRONG_NS_ON_IMPORT", "'" + namespace + "'", "'" + importTargetNS + "'")); + element = null; + importInvalid = true; + } + } + + catch(Exception e) + { + } + finally + { + if(reader != null) + { + try + { + reader.close(); + } + catch(IOException e) + {} + } + } + + } + return element; + } + + /** + * Create an import element for a WSDL import of a WSDL document. + * + * @param wsdlDocument The document of the import. + * @return The newly created import element. + */ + public Import createWSDLImport(WSDLDocument wsdlDocument) + { + if(importDef != null) + { + return importDef; + } + importDef = getNewImport(); + + if (importDef != null) + { + importDef.setDefinition(wsdlDocument.getDefinition()); + schemas.addAll(wsdlDocument.getSchemas()); + importingWSDLDoc.addSchemas(schemas); + } + + return importDef; + } + + /** + * Create an import element for a WSDL import of a schema import of a schema document. + * + * @return The newly created import element. + */ + public Import createXSDImport() + { + if(importDef != null) + { + return importDef; + } + importDef = getNewImport(); + XSDValidator xsdvalidator = new XSDValidator(); + + xsdvalidator.validate(location, XMLCatalogResolver.getInstance()); + if (xsdvalidator.isValid()) + { + XSModel schema = xsdvalidator.getXSModel(); + if (schema != null) + { + schemas.add(schema); + } + } + else + { + // addReaderWarning( +// def, +// importDef, +// messagegenerator.getString("_UNABLE_TO_IMPORT_INVALID", "'" + importDef.getLocationURI() + "'")); + Iterator errors = xsdvalidator.getErrors().iterator(); + while (errors.hasNext()) + { + ErrorMessage err = (ErrorMessage) errors.next(); + String uri = err.getURI(); + int line = err.getErrorLine(); + String errmess = err.getErrorMessage(); + valinfo.addError(errmess, line, err.getErrorColumn(), uri); + } + } + importingWSDLDoc.addSchemas(schemas); + return importDef; + } + + /** + * Get the import element if it has been created. + * + * @return The import element if it has been created or null. + */ + public Import getImport() + { + return importDef; + } + + /** + * Get a new import element. + * + * @return A new import element. + */ + private Import getNewImport() + { + if(importInvalid) + { + return null; + } + Import importDef = importingDef.createImport(); + + if (namespace != null) + { + importDef.setNamespaceURI(namespace); + } + + if (location != null) + { + importDef.setLocationURI(location); + } + + if(element != null) + { + Element tempEl = DOMUtils.getFirstChildElement(element); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + importDef.setDocumentationElement(tempEl); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + } + + return importDef; + } + + /** + * Get the schemas corresponding to this import. + * + * @return The schemas corresponding to this import. + */ + public List getSchemas() + { + return schemas; + } + + /** + * Returns true if this import imports a WSDL document, false otherwise. + * + * @return True if this import imports a WSDL document, false otherwise. + */ + public boolean isWSDLFileImport() + { + return isWSDLFileImport; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) + { + if(obj.getClass() == ImportHolder.class) + { + ImportHolder otherImport = (ImportHolder)obj; + + if(getNamespace().equals(otherImport.getNamespace()) && getLocation().equals(otherImport.getLocation())) + { + return true; + } + } + return false; + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(Object obj) + { + if(obj == null) + { + throw new NullPointerException(); + } + + ImportHolder otherImport = (ImportHolder)obj; + + return (getNamespace()+getLocation()).compareTo((otherImport.getNamespace()+otherImport.getLocation())); + } + + /** + * Set the messagegenerator for the import holder. + * + * @param mg The message generator to set. + */ + public void setMessageGenerator(MessageGenerator mg) + { + messagegenerator = mg; + } + + /** + * Return true if the import is invalid, false otherwise. + * + * @return True if the import is invalid, false otherwise. + */ + public boolean isImportInvalid() + { + return importInvalid; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/LocationHolder.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/LocationHolder.java new file mode 100644 index 000000000..1def333f9 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/LocationHolder.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +/** + * Holds the location information for an element in a document. + */ +public class LocationHolder +{ + private int line; + private int column; + private String uri; + + /** + * Constructor. + * + * @param line The line number. + * @param column The column number. + * @param uri The URI of the document. + */ + public LocationHolder(int line, int column, String uri) + { + this.line = line; + this.column = column; + this.uri = uri; + } + + /** + * Get the line number. + * + * @return The line number. + */ + public int getLine() + { + return line; + } + + /** + * Get the column number. + * + * @return The column number. + */ + public int getColumn() + { + return column; + } + + /** + * Get the file URI. + * + * @return The file URI. + */ + public String getURI() + { + return uri; + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/ReaderError.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/ReaderError.java new file mode 100644 index 000000000..36b4e44dc --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/ReaderError.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +/** + * Holds an error created when reading a WSDL document. + */ +public class ReaderError +{ + protected Object parentObject; // the object of the parent of the object with the error + protected Object object; // the object the error is associated with + protected String error; // the error associated with the object + + /** + * Constructor. + * + * @param parentObject the parent object of the object with the error + * @param object the object with the error + * @param error the error + */ + public ReaderError(Object parentObject, Object object, String error) + { + this.parentObject = parentObject; + this.object = object; + this.error = error; + } + + /** + * Returns the parent object of the object with the error. + * + * @return the parent object of the object with the error + */ + public Object getParentObject() + { + return parentObject; + } + + /** + * Returns the object with the error. + * + * @return the object with the error + */ + public Object getObject() + { + return object; + } + + /** + * Returns the error message. + * + * @return the error message + */ + public String getError() + { + return error; + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/ValidatorRegistry.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/ValidatorRegistry.java new file mode 100644 index 000000000..90564f483 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/ValidatorRegistry.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +import java.util.Hashtable; +import java.util.Map; + + +/** + * A registry to hold all the WSDL 1.1 validators. + */ +public class ValidatorRegistry +{ + + protected static ValidatorRegistry verInstance; + + protected Map validatorReg = new Hashtable(); + + /** + * Constructor. + */ + protected ValidatorRegistry() + { + } + + /** + * Returns the instance of this registry. + * + * @return The instance of this registry. + */ + public static ValidatorRegistry getInstance() + { + if (verInstance == null) + { + verInstance = new ValidatorRegistry(); + } + return verInstance; + } + + /** + * Register this validator delegate with the given namespace. + * + * @param namespace The namespace the validator is associated with. + * @param valDelegate The validator delegate to register. + */ + public void registerValidator(String namespace, WSDL11ValidatorDelegate valDelegate) + { + // allow the null namespace but make it the empty string + if (namespace == null) + { + namespace = ""; + } + + // add the validator to the hashtable + validatorReg.put(namespace, valDelegate); + } + + /** + * Ask for the validator associated with this namespace. If none is found + * return null. + * + * @param namespace The namespace of the validator. + * @return The WSDL 1.1 validator for the given namespace. + */ + public IWSDL11Validator queryValidatorRegistry(String namespace) + { + // if the namespace is null allow it and treat it as the empty string + if (namespace == null) + { + namespace = ""; + } + WSDL11ValidatorDelegate delegate = (WSDL11ValidatorDelegate)validatorReg.get(namespace); + if(delegate != null) + { + return delegate.getValidator(); + } + return null; + } + + /** + * Convenience method that tells whether a validator for a given namespace is registered. + * + * @param namespace The namespace to check. + * @return True if there is a validator registered, false otherwise. + */ + public boolean hasRegisteredValidator(String namespace) + { + if (queryValidatorRegistry(namespace) != null) + { + return true; + } + return false; + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11BasicValidator.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11BasicValidator.java new file mode 100644 index 000000000..4d7b0c238 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11BasicValidator.java @@ -0,0 +1,691 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.Vector; + +import javax.wsdl.Binding; +import javax.wsdl.BindingFault; +import javax.wsdl.BindingInput; +import javax.wsdl.BindingOperation; +import javax.wsdl.BindingOutput; +import javax.wsdl.Definition; +import javax.wsdl.Fault; +import javax.wsdl.Input; +import javax.wsdl.Message; +import javax.wsdl.Operation; +import javax.wsdl.Output; +import javax.wsdl.Part; +import javax.wsdl.Port; +import javax.wsdl.PortType; +import javax.wsdl.Service; +import javax.wsdl.Types; +import javax.wsdl.extensions.ExtensibilityElement; +import javax.xml.namespace.QName; + +import org.apache.xerces.xs.XSModel; +import org.eclipse.wst.wsdl.validation.internal.util.MessageGenerator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd.SchemaAttributeTable; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd.XSDValidator; + +import com.ibm.wsdl.Constants; +/** + * Validate the elements defined in a WSDL 1.1 Document. + */ +public class WSDL11BasicValidator implements IWSDL11Validator +{ + protected final String NAMESPACES_FEATURE_ID = "http://xml.org/sax/features/namespaces"; + protected final String VALIDATION_FEATURE_ID = "http://xml.org/sax/features/validation"; + protected final String SCHEMA_VALIDATION_FEATURE_ID = "http://apache.org/xml/features/validation/schema"; + protected final String SCHEMA_FULL_CHECKING_FEATURE_ID = + "http://apache.org/xml/features/validation/schema-full-checking"; + protected final String CONTINUE_AFTER_FATAL_ERROR_ID = "http://apache.org/xml/features/continue-after-fatal-error"; + protected final String SOAP_ENCODING_URI = "http://schemas.xmlsoap.org/soap/encoding/"; + + // Error and Warning Keys + private final String _PORT_NAME_NOT_UNIQUE = "_PORT_NAME_NOT_UNIQUE"; + private final String _NO_BINDING_FOR_PORT = "_NO_BINDING_FOR_PORT"; + private final String _NO_ADDRESS_PORT = "_NO_ADDRESS_PORT"; + private final String _MORE_THEN_ONE_ADDRESS_PORT = "_MORE_THEN_ONE_ADDRESS_PORT"; //TODO should be _MORE_THAN_ONE_ADDRESS_PORT, not THEN + private final String _PORTTYPE_UNDEFINED_FOR_BINDING = "_PORTTYPE_UNDEFINED_FOR_BINDING"; + private final String _OPERATION_UNDEFINED_FOR_PORTTYPE = "_OPERATION_UNDEFINED_FOR_PORTTYPE"; + private final String _OPERATION_NO_INPUT_OR_OUTPUT = "_OPERATION_NO_INPUT_OR_OUTPUT"; + private final String _INPUT_NAME_NOT_UNIQUE = "_INPUT_NAME_NOT_UNIQUE"; + private final String _MESSAGE_UNDEFINED_FOR_INPUT = "_MESSAGE_UNDEFINED_FOR_INPUT"; + private final String _OUTPUT_NAME_NOT_UNIQUE = "_OUTPUT_NAME_NOT_UNIQUE"; + private final String _MESSAGE_UNDEFINED_FOR_OUTPUT = "_MESSAGE_UNDEFINED_FOR_OUTPUT"; + private final String _MESSAGE_UNDEFINED_FOR_FAULT = "_MESSAGE_UNDEFINED_FOR_FAULT"; + private final String _PART_NO_ELEMENT_OR_TYPE = "_PART_NO_ELEMENT_OR_TYPE"; + private final String _PART_BOTH_ELEMENT_AND_TYPE = "_PART_BOTH_ELEMENT_AND_TYPE"; + private final String _PART_INVALID_ELEMENT = "_PART_INVALID_ELEMENT"; + private final String _PART_INVALID_TYPE = "_PART_INVALID_TYPE"; + private final String _WARN_SOAPENC_IMPORTED_PART = "_WARN_SOAPENC_IMPORTED_PART"; + + private final int ELEMENT = 0; + private final int TYPE = 1; + + private final String REQUEST = "Request"; + private final String RESPONSE = "Response"; + private final String QUOTE = "'"; + private final String EMPTY_STRING = ""; + + + //protected WSDL11ValidatorController validatorcontroller; + protected MessageGenerator messagegenerator; + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11Validator#validate(java.lang.Object, java.util.List, org.eclipse.wsdl.validate.wsdl11.IWSDL11ValidationInfo) + */ + public void validate(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + //this.validatorcontroller = validatorcontroller; + //setDefaultResourceBundleIfNeeded(validatorcontroller); + Definition wsdlDefinition = (Definition)element; + //validateTypes(wsdlDefinition, valInfo); + validateServices(wsdlDefinition, valInfo); + validateBindings(wsdlDefinition, valInfo); + validatePortTypes(wsdlDefinition, valInfo); + validateMessages(wsdlDefinition, valInfo); + + } + + /** + * Takes a list of ExtensibilityElements and checks if there's a validator + * associated with each element and if so calls the validator. + * + * @param parents The list of parents of the elements. + * @param extensibilityElements The list of elements to validate. + * @param validatorcontroller The validator controller. + * @param wsdlDefinition The defnintions element for this document. + */ + protected void validateExtensibilityElementList( + List parents, + List extensibilityElements, + IWSDL11ValidationInfo valInfo) + { + ValidatorRegistry ver = ValidatorRegistry.getInstance(); + Iterator extElems = extensibilityElements.iterator(); + while (extElems.hasNext()) + { + ExtensibilityElement element = (ExtensibilityElement)extElems.next(); + String namespace = element.getElementType().getNamespaceURI(); + IWSDL11Validator val = ver.queryValidatorRegistry(namespace); + if (val != null) + { + val.validate(element, parents, valInfo); + } +// else +// { +// valInfo.addNamespaceWithNoValidator(namespace); +// } + } + } + + /** + * If the resourcebundle hasn't been set, set it to the one registered with the ValidatorController. + * + * @param validatorcontroller The validator controller to get the resource bundle from. + */ + // protected void setDefaultResourceBundleIfNeeded(WSDL11ValidatorController validatorcontroller) + // { + // if (messagegenerator == null) + // { + // setResourceBundle(validatorcontroller.getResourceBundle()); + // } + // } + + /** + * Set the resourcebundle to the one specified. + * + * @param rb The resource bundle to set. + */ + public void setResourceBundle(ResourceBundle rb) + { + messagegenerator = new MessageGenerator(rb); + } + + /** + * Ensure that the Types element is correct. + * + * @param wsdlDefinition The definitions element from the current document. + */ + + protected void validateTypes(Definition wsdlDefinition, IWSDL11ValidationInfo valInfo) + { + Types types = wsdlDefinition.getTypes(); + // ensure that types is defined + if (types != null) + { + List parents = new Vector(); + parents.add(wsdlDefinition); + Object extensibleElements[] = types.getExtensibilityElements().toArray(); + parents.add(0, types); + validateExtensibilityElementList(parents, types.getExtensibilityElements(), valInfo); + parents.remove(0); + } + } + + /** + * Validates all of the declared services for the definition. + * + * @param wsdlDefinition The WSDL definitions element. + */ + protected void validateServices(Definition wsdlDefinition, IWSDL11ValidationInfo valInfo) + { + if (wsdlDefinition.getServices() == null) + return; + Object services[] = wsdlDefinition.getServices().values().toArray(); + List parents = new Vector(); + parents.add(wsdlDefinition); + Hashtable allPorts = new Hashtable(); + + //TODO: check that ports in other imported files don't conflict with ports in this one + // // register all of the imported ports + // Iterator imports = wsdlDefinition.getImports().values().iterator(); + // while(imports.hasNext()) + // { + // Iterator impservices = ((Import)imports.next()).getDefinition().getServices().values().iterator(); + // while(impservices.hasNext()) + // { + // Iterator impports = ((Service)impservices.next()).getPorts().values().iterator(); + // while(impports.hasNext()) + // { + // Port tempP = (Port)impports.next(); + // allPorts.put(tempP.getName(),tempP); + // } + // } + // } + for (int i = 0; i < services.length; i++) + { + Service s = (Service)services[i]; + parents.add(0, s); + Object ports[] = s.getPorts().values().toArray(); + HashSet portInputs = new HashSet(); + HashSet portOutputs = new HashSet(); + for (int j = 0; j < ports.length; j++) + { + Port p = (Port)ports[j]; + parents.add(0, p); + // a Port name must be unique within the entire WDSL document + if (allPorts.contains(p.getName())) + { String[] args = {p.getName()}; + + valInfo.addError(messagegenerator.getString(_PORT_NAME_NOT_UNIQUE, QUOTE + args[0] + QUOTE), + p, _PORT_NAME_NOT_UNIQUE, args); + } + else + { + allPorts.put(p.getName(), p); + + // get the binding for this port and see if the PortType for the binding + // is defined + if (p.getBinding() == null || p.getBinding().isUndefined()) + { + String bindingName = EMPTY_STRING; + if (p.getBinding() != null) + { + bindingName = p.getBinding().getQName().getLocalPart(); + } + String args[] = {p.getName()}; + valInfo.addError( + messagegenerator.getString(_NO_BINDING_FOR_PORT, QUOTE + args[0] + QUOTE, QUOTE + bindingName + QUOTE), + p, _NO_BINDING_FOR_PORT, args); + } + else + { + // TODO: Check that the output of one port isn't the input of another and vice versa + // extensibility elements the port + // there can only be one and must be one extensibility element defined for a port + List extelems = p.getExtensibilityElements(); + if (extelems.size() < 1) + { String args[]= {p.getName()}; + valInfo.addError(messagegenerator.getString(_NO_ADDRESS_PORT, QUOTE + args[0] + QUOTE), + p, _NO_ADDRESS_PORT, args); + } + else if (extelems.size() > 1) + { + for (int k = 1; k < extelems.size(); k++) + { + String[] args = {p.getName()}; + valInfo.addError( + messagegenerator.getString(_MORE_THEN_ONE_ADDRESS_PORT, QUOTE + args[0] + QUOTE), + extelems.get(k), _MORE_THEN_ONE_ADDRESS_PORT, args); + } + } + validateExtensibilityElementList(parents, p.getExtensibilityElements(), valInfo); + } + } + + parents.remove(0); + } + // extensibility elements for the service + validateExtensibilityElementList(parents, s.getExtensibilityElements(), valInfo); + parents.remove(0); + } + } + + /** + * Checks that the bindings refer to valid PortTypes and all of the operations + // in a given binding refer to a defined operation within the corresponding + // PortType. + * + * @param wsdlDefinition The WSDL definitions element. + */ + protected void validateBindings(Definition wsdlDefinition, IWSDL11ValidationInfo valInfo) + { + if (wsdlDefinition.getBindings() == null) + return; + Object bindings[] = wsdlDefinition.getBindings().values().toArray(); + List parents = new Vector(); + parents.add(wsdlDefinition); + for (int i = 0; i < bindings.length; i++) + { + Binding b = (Binding)bindings[i]; + parents.add(0, b); + PortType portType = b.getPortType(); + + if (portType == null) + { + continue; + } + // the PortType is not defined so don't bother checking the operations + if (portType.isUndefined()) + { String[] args = {portType.getQName().getLocalPart(), b.getQName().getLocalPart()}; + valInfo.addError( + messagegenerator.getString( + _PORTTYPE_UNDEFINED_FOR_BINDING, + QUOTE + args[0] + QUOTE, + QUOTE + args[1] + QUOTE), + b, _PORTTYPE_UNDEFINED_FOR_BINDING, args); + } + else + { + // the PortType is defined so now we have to check that the operations are defined + Object bindingOperations[] = b.getBindingOperations().toArray(); + + // check if the operation is defined for each BindingOperation + for (int k = 0; k < bindingOperations.length; k++) + { + BindingOperation bo = (BindingOperation)bindingOperations[k]; + parents.add(0, bo); + if (bo.getOperation() == null || bo.getOperation().isUndefined()) + { + String[] args = {b.getQName().getLocalPart(), portType.getQName().getLocalPart()}; + valInfo.addError( + messagegenerator.getString( + _OPERATION_UNDEFINED_FOR_PORTTYPE, + QUOTE + args[0] + QUOTE, + QUOTE + args[1] + QUOTE), + bo, _OPERATION_UNDEFINED_FOR_PORTTYPE, args); + // nice idea to add suggestions to other elements to fix the error + // but it doesn't work with multipe files like this + //addValidationMessage(warningList,portType,portType.getQName().getLocalPart() + "Define an operation here to correspond with the operation in: " + bo.getName()); + } + // take care of all the extensibility elements in the binding operation, binding inputs, outputs and faults + else + { + BindingInput binput = bo.getBindingInput(); + if (binput != null) + { + parents.add(0, binput); + // extensibility elements for binding operation input + validateExtensibilityElementList( + parents, + bo.getBindingInput().getExtensibilityElements(), + valInfo); + parents.remove(0); + } + BindingOutput boutput = bo.getBindingOutput(); + if (boutput != null) + { + parents.add(0, boutput); + // extensibility elements for binding operation output + validateExtensibilityElementList( + parents, + bo.getBindingOutput().getExtensibilityElements(), + valInfo); + parents.remove(0); + } + // no input or output has been defined for the operation + if (binput == null && boutput == null) + { String[] args = { bo.getName() }; + valInfo.addError( + messagegenerator.getString(_OPERATION_NO_INPUT_OR_OUTPUT, QUOTE + args[0] + QUOTE), + bo, _OPERATION_NO_INPUT_OR_OUTPUT, args); + } + // extensibility elements for each binding operation fault + Iterator faults = bo.getBindingFaults().values().iterator(); + while (faults.hasNext()) + { + BindingFault bf = (BindingFault)faults.next(); + parents.add(0, bf); + validateExtensibilityElementList(parents, bf.getExtensibilityElements(), valInfo); + parents.remove(0); + } + } + // extensibility elements for binding operation + validateExtensibilityElementList(parents, bo.getExtensibilityElements(), valInfo); + parents.remove(0); + } + } + // extensibility elements for the binding + validateExtensibilityElementList(parents, b.getExtensibilityElements(), valInfo); + parents.remove(0); + } + + } + + /** + * Check that all of the PortTypes have valid messages associated with their + // operation input, output and fault types. + * + * @param wsdlDefinition The WSDL definitions element. + */ + protected void validatePortTypes(Definition wsdlDefinition, IWSDL11ValidationInfo valInfo) + { + if (wsdlDefinition.getPortTypes() == null) + return; + Object porttypes[] = wsdlDefinition.getPortTypes().values().toArray(); + + for (int i = 0; i < porttypes.length; i++) + { + PortType p = (PortType)porttypes[i]; + Object operations[] = p.getOperations().toArray(); + List inAndOutNames = new Vector(); + for (int j = 0; j < operations.length; j++) + { + Operation o = (Operation)operations[j]; + if (o == null || o.isUndefined()) + { + continue; + } + + // check that the messages are defined for the input, output and faults + Message m; + Input input = o.getInput(); + if (input != null) + { + String inputName = input.getName(); + // if the name isn't defined it defaults to this + if (inputName == null) + { + inputName = o.getName() + REQUEST; + } + if (inAndOutNames.contains(inputName)) + { String[] args = {inputName, p.getQName().getLocalPart() }; + valInfo.addError( + messagegenerator.getString( + _INPUT_NAME_NOT_UNIQUE, + QUOTE + args[0] + QUOTE, + QUOTE + args[1] + QUOTE), + input, _INPUT_NAME_NOT_UNIQUE, args); + } + else + { + inAndOutNames.add(inputName); + } + + m = input.getMessage(); + if (m != null && m.isUndefined()) + { + String messName = EMPTY_STRING; + QName messQName = m.getQName(); + if (messQName != null) + { + messName = messQName.getLocalPart(); + } + String[] args = {messName}; + valInfo.addError(messagegenerator.getString(_MESSAGE_UNDEFINED_FOR_INPUT, QUOTE + args[0] + QUOTE), + input, _MESSAGE_UNDEFINED_FOR_INPUT, args); + } + } + Output output = o.getOutput(); + if (output != null) + { + String outputName = output.getName(); + // if the name isn't defined it defaults to this + if (outputName == null) + { + outputName = o.getName() + RESPONSE; + } + + if (inAndOutNames.contains(outputName)) + { + String[] args = {outputName, p.getQName().getLocalPart()}; + + valInfo.addError( + messagegenerator.getString( + _OUTPUT_NAME_NOT_UNIQUE, + QUOTE + args[0] + QUOTE, + QUOTE + args[1] + QUOTE), + output, _OUTPUT_NAME_NOT_UNIQUE, args); + } + else + { + inAndOutNames.add(outputName); + } + + m = output.getMessage(); + if (m != null && m.isUndefined()) + { + String messName = EMPTY_STRING; + QName messQName = m.getQName(); + if (messQName != null) + { + messName = messQName.getLocalPart(); + } + String[] args = {messName}; + valInfo.addError(messagegenerator.getString(_MESSAGE_UNDEFINED_FOR_OUTPUT, QUOTE + args[0] + QUOTE), + output, _MESSAGE_UNDEFINED_FOR_OUTPUT, args); + } + } + + Object faults[] = o.getFaults().values().toArray(); + + //List faultNames = new Vector(); + for (int k = 0; k < faults.length; k++) + { + Fault f = (Fault)faults[k]; + m = f.getMessage(); + if (m != null && m.isUndefined()) + { + String messName = EMPTY_STRING; + QName messQName = m.getQName(); + if (messQName != null) + { + messName = messQName.getLocalPart(); + } + String args[] = {messName}; + valInfo.addError(messagegenerator.getString(_MESSAGE_UNDEFINED_FOR_FAULT, QUOTE + args[0] + QUOTE), + f, _MESSAGE_UNDEFINED_FOR_FAULT, args); + } + } + } + } + } + + /** + * Check that all the messages defined in the WSDL document are valid. + * + * @param wsdlDefinition The WSDL definitions element. + */ + protected void validateMessages(Definition wsdlDefinition, IWSDL11ValidationInfo valInfo) + { + if (wsdlDefinition.getMessages() == null) + return; + Iterator messages = wsdlDefinition.getMessages().values().iterator(); + + while (messages.hasNext()) + { + Message m = (Message)messages.next(); + if (!m.isUndefined()) + { + // if the message has a part (and it doesn't have to) + // ensure each message part has either an element or a type + if (!m.getParts().isEmpty()) + { + Iterator parts = m.getParts().values().iterator(); + while (parts.hasNext()) + { + Part p = (Part)parts.next(); + QName elementName = p.getElementName(); + QName typeName = p.getTypeName(); + Map extAtts = p.getExtensionAttributes(); + // TODO:This will have to be extended as parts can have extensibility elements + //ensure the part has a type or an element defined + if (elementName == null && typeName == null && (extAtts == null || extAtts.isEmpty())) + { String[] args = { p.getName()}; + valInfo.addError(messagegenerator.getString(_PART_NO_ELEMENT_OR_TYPE, QUOTE + args[0] + QUOTE), + p, _PART_NO_ELEMENT_OR_TYPE, args); + } + //here the part has both the element and type defined and it can only have one defined + else if (elementName != null && typeName != null) + { String[] args = {p.getName()}; + valInfo.addError(messagegenerator.getString(_PART_BOTH_ELEMENT_AND_TYPE, QUOTE + args[0] + QUOTE), + p, _PART_BOTH_ELEMENT_AND_TYPE, args); + } + else if (elementName != null) + { + if (!checkPartConstituent(elementName.getNamespaceURI(), + elementName.getLocalPart(), + ELEMENT, + p, + valInfo)) + { String[] args = {p.getName(), elementName.getLocalPart()}; + valInfo.addError( + messagegenerator.getString( + _PART_INVALID_ELEMENT, + QUOTE + args[0] + QUOTE, + QUOTE + args[1] + QUOTE), + p, _PART_INVALID_ELEMENT, args); + } + } + else if (typeName != null) + { + // check that the type itself is defined properly + if (!checkPartConstituent(typeName.getNamespaceURI(), typeName.getLocalPart(), TYPE, p, valInfo)) + { String[] args = {p.getName(), typeName.getLocalPart() }; + valInfo.addError( + messagegenerator.getString( + _PART_INVALID_TYPE, + QUOTE + args[0] + QUOTE, + QUOTE + args[1] + QUOTE), + p, _PART_INVALID_TYPE, args); + } + } + } + } + } + } + } + + /** + * Checks whether the given name is defined in the namespace for the part. A part is an + * ELEMENT or a TYPE. + * + * @param namespace The namespace to check. + * @param name The name to check. + * @param part The part to check, either ELEMENT or TYPE. + * @param partObject The object representing the given part. + * @return True if the part element of type is defined, false otherwise. + */ + protected boolean checkPartConstituent( + String namespace, + String name, + int part, + Part partObject, + IWSDL11ValidationInfo valInfo) + { + + boolean partvalid = false; + // First take care of the situation where it's from the schema namespace. + // The 1999, 2000 and 2001 schema namespaces are all accepted. + if (namespace.equals(Constants.NS_URI_XSD_2001) + || namespace.equals(Constants.NS_URI_XSD_1999) + || namespace.equals(Constants.NS_URI_XSD_2000)) + { + SchemaAttributeTable xsdTable = new SchemaAttributeTable(); + if (xsdTable.containsSymbol(name)) + { + partvalid = true; + } + } + // check inline and imported schema + else + { + XSModel[] schemas = valInfo.getSchemas(); + int numSchemas = schemas.length; + //Iterator schemasIter = schemas.iterator(); + for (int i = 0; i < numSchemas; i++) + { + XSModel schema = schemas[i]; + if (schema != null) + { + if (part == ELEMENT && schema.getElementDeclaration(name, namespace) != null) + { + partvalid = true; + break; + } + else if (part == TYPE && schema.getTypeDefinition(name, namespace) != null) + { + partvalid = true; + break; + } + } + } + } + // If the SOAP encoding namespace hasn't been explicitly imported do so + // now. + // Allow the SOAP encoding namespace to be automatically imported but mark + // it as a warning. + if (!partvalid && namespace.equals(SOAP_ENCODING_URI)) + { + try + { + XSDValidator xsdVal = new XSDValidator(); + String soapEnc = valInfo.getURIResolver().resolve(null, SOAP_ENCODING_URI, null).getPhysicalLocation(); + if(soapEnc != null) + { + xsdVal.validate(soapEnc, null); + // sanity check in case something goes wrong + if (xsdVal.isValid()) + { + XSModel xsModel = xsdVal.getXSModel(); + + if (part == ELEMENT && xsModel.getElementDeclaration(name, namespace) != null) + { + partvalid = true; + } + else if (part == TYPE && xsModel.getTypeDefinition(name, namespace) != null) + { + partvalid = true; + } + valInfo.addWarning(messagegenerator.getString(_WARN_SOAPENC_IMPORTED_PART, QUOTE + name + QUOTE), partObject); + } + } + } + catch (Exception e) + { + //TODO: log the error message + //System.out.println(e); + } + } + return partvalid; + } + +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11ValidationInfoImpl.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11ValidationInfoImpl.java new file mode 100644 index 000000000..4b30f0953 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11ValidationInfoImpl.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +import java.util.Hashtable; +import java.util.List; +import java.util.Vector; + +import org.apache.xerces.xs.XSModel; +import org.eclipse.wst.wsdl.validation.internal.IValidationInfo; +import org.eclipse.wst.wsdl.validation.internal.ValidationInfoImpl; +import org.eclipse.wst.wsdl.validation.internal.resolver.URIResolver; + +/** + * An implemenation of WSDL11ValidationInfo. + */ +public class WSDL11ValidationInfoImpl implements IWSDL11ValidationInfo +{ + private IValidationInfo valinfo = null; + private Hashtable elementlocations = null; + private List schemas = new Vector(); + + public WSDL11ValidationInfoImpl(IValidationInfo valinfo) + { + this.valinfo = valinfo; + } + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo#getFileURI() + */ + public String getFileURI() + { + return valinfo.getFileURI(); + } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo#addSchema(org.apache.xerces.xs.XSModel) + */ + public void addSchema(XSModel xsModel) + { + if (xsModel != null) + { + schemas.add(xsModel); + } + + } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo#getSchemas() + */ + public XSModel[] getSchemas() + { + return (XSModel[])schemas.toArray(new XSModel[schemas.size()]); + } + + /* (non-Javadoc) + * @see org.eclipse.wsdl.validate.wsdl11.WSDL11ValidationInfo#cleardSchemas() + */ + public void clearSchemas() + { + schemas.clear(); + } + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo#setElementLocations(java.util.Hashtable) + */ + public void setElementLocations(Hashtable elementLocations) + { + this.elementlocations = elementLocations; + } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo#addError(java.lang.String, java.lang.Object) + */ + public void addError(String message, Object element) + { + addError(message, element, null, null); + } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo#addError(java.lang.String, java.lang.Object, java.lang.String, java.lang.Object[]) + */ + public void addError(String message, Object element, String errorKey, Object[] messageArguments) + { + LocationHolder location; + if (elementlocations.containsKey(element)) + { + location = (LocationHolder)elementlocations.get(element); + addError(message, location.getLine(), location.getColumn(), location.getURI(), errorKey, messageArguments); + } + // if we give it an element that hasn't been defined we'll set the location + // at (0,0) so the error shows up but no line marker in the editor + else + { + addError(message, 0, 1, getFileURI()); + } + } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo#addWarning(java.lang.String, java.lang.Object) + */ + public void addWarning(String message, Object element) + { + LocationHolder location; + if (elementlocations.containsKey(element)) + { + location = (LocationHolder)elementlocations.get(element); + addWarning(message, location.getLine(), location.getColumn(), location.getURI()); + } + // if we give it an element that hasn't been defined we'll set the location + // at (0,0) so the error shows up but no line marker in the editor + else + { + addWarning(message, 0, 1, getFileURI()); + } + + } + + /** + * @see org.eclipse.wsdl.validate.wsdl11.WSDL11ValidationInfo#addNamespaceWithNoValidator(java.lang.String) + */ +// public void addNamespaceWithNoValidator(String namespace) +// { +// valinfo.addNamespaceWithNoValidator(namespace); +// +// } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo#addError(java.lang.String, int, int) + */ + public void addError(String message, int line, int column, String uri) + { + addError(message, line, column, uri, null, null); + } + + public void addError(String message, int line, int column, String uri, String errorKey, Object[] messageArguments) + { + try + { ((ValidationInfoImpl)valinfo).addError(message, line, column, uri, errorKey, messageArguments); + } + catch (ClassCastException e) + { System.err.println(e); + valinfo.addError(message, line, column, uri); + } + } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo#addWarning(java.lang.String, int, int) + */ + public void addWarning(String message, int line, int column, String uri) + { + valinfo.addWarning(message, line, column, uri); + } + + /* (non-Javadoc) + * @see org.eclipse.wsdl.validate.wsdl11.WSDL11ValidationInfo#getURIResolver() + */ + public URIResolver getURIResolver() + { + return valinfo.getURIResolver(); + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11ValidatorController.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11ValidatorController.java new file mode 100644 index 000000000..aa46e81bb --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11ValidatorController.java @@ -0,0 +1,420 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +import java.util.Iterator; +import java.util.List; +import java.util.ResourceBundle; +import java.util.Vector; + +import javax.wsdl.Definition; +import javax.wsdl.WSDLException; + +import org.apache.xerces.xs.XSModel; +import org.eclipse.wst.wsdl.validation.internal.IWSDLValidator; +import org.eclipse.wst.wsdl.validation.internal.IValidationInfo; +import org.eclipse.wst.wsdl.validation.internal.exception.ValidateWSDLException; +import org.eclipse.wst.wsdl.validation.internal.util.MessageGenerator; +import org.w3c.dom.Document; + +import com.ibm.wsdl.Constants; + +/** + * The validator controller is the head of validation. + */ +public class WSDL11ValidatorController implements IWSDLValidator +{ + protected final String _WARN_NO_VALDIATOR = "_WARN_NO_VALDIATOR"; +// protected final int ERROR_MESSAGE = 0; +// protected final int WARNING_MESSAGE = 1; +// protected String fileURI; +// protected List schemas = new Vector(); +// protected Definition wsdlDefinition; + protected MessageGenerator messagegenerator = null; + //protected ValidationController validationController; + protected ValidatorRegistry ver = ValidatorRegistry.getInstance(); + + /** + * Constructor. + */ + public WSDL11ValidatorController() + { + } + + + /* (non-Javadoc) + * @see org.eclipse.wsdl.validate.IWSDLValidator#validate(org.w3c.dom.Document, org.eclipse.wsdl.validate.ValidationInfo) + */ + public void validate(Document domModel, IValidationInfo valInfo) throws ValidateWSDLException + { + // reset the variables +// reset(); +// fileURI = valInfo.getFileURI(); + //this.validationController = validationcontroller; + + IWSDL11ValidationInfo wsdlvalinfo = new WSDL11ValidationInfoImpl(valInfo); + WSDLDocument[] wsdlDocs = readWSDLDocument(domModel, valInfo.getFileURI(), getMessageGenerator(), wsdlvalinfo); + // Don't validate an null definitions element. Either the file is emtpy and valid or + // had an error when reading. + if(wsdlDocs != null) + { + int numWSDLDocs = wsdlDocs.length; + for(int i = 0; i < numWSDLDocs; i++) + { + WSDLDocument tempDoc = wsdlDocs[i]; + Definition wsdlDefinition = tempDoc.getDefinition(); + // Register the schemas. + List xsdList = tempDoc.getSchemas(); + Iterator xsdIter = xsdList.iterator(); + while (xsdIter.hasNext()) + { + wsdlvalinfo.addSchema((XSModel)xsdIter.next()); + } + // Set the element locations table. + wsdlvalinfo.setElementLocations(tempDoc.getElementLocations()); + // Set any reader errors. This needs to be done after the element locations table is set. + List readerErrors = tempDoc.getReaderErrors(); + if (readerErrors != null) + { + Iterator readerErrorsI = readerErrors.iterator(); + while (readerErrorsI.hasNext()) + { + ReaderError re = (ReaderError)readerErrorsI.next(); + wsdlvalinfo.addError(re.getError(), re.getObject()); + } + } + List readerWarnings = tempDoc.getReaderWarnings(); + if (readerWarnings != null) + { + Iterator readerWarningsI = readerWarnings.iterator(); + while (readerWarningsI.hasNext()) + { + ReaderError re = (ReaderError)readerWarningsI.next(); + wsdlvalinfo.addWarning(re.getError(), re.getObject()); + } + } + validateWSDLElement(Constants.NS_URI_WSDL, wsdlDefinition, new Vector(), wsdlvalinfo); + wsdlvalinfo.clearSchemas(); + } + } + + } + + /** + * Validate an imported WSDL document. Allows the calling class to have access to the internal + * components of the validation. + * + * @param wsdlvalinfo The WSDL 1.1 validation info object to use. + * @return The definitions element for the import. + * @throws ValidateWSDLException + */ +// protected Definition validateImport(WSDL11ValidationInfo wsdlvalinfo) +// { +// WSDLDocument[] wsdlDocs = null; +// try +// { +// wsdlDocs = readWSDLDocument(null, wsdlvalinfo.getFileURI(), getMessageGenerator(), wsdlvalinfo); +// } +// catch(ValidateWSDLException e) +// { +// // supress any validation issues with imported documents +// } +// // Don't validate an null definitions element. Either the file is emtpy and valid or +// // had an error when reading. +// if(wsdlDocs != null) +// { +// validateWSDLElement(Constants.NS_URI_WSDL, wsdlDefinition, new Vector(), wsdlvalinfo); +// } +// return wsdlDefinition; +// } + + /** + * Read in the WSDL document and set the model and imported schemas. + * + * @param domModel A DOM model of the document to be read. + * @param file The file to read. + * @param messagegenerator The messagegenerator the reader should use for any messages produced. + * @param wsdlvalinfo The validation information for the current validation. + * @return The definitions element for the WSDL document. + * @throws ValidateWSDLException + */ + protected WSDLDocument[] readWSDLDocument(Document domModel, String file, MessageGenerator messagegenerator, IWSDL11ValidationInfo wsdlvalinfo) throws ValidateWSDLException + { + WSDLDocument[] wsdlDocs = null; + try + { + + WSDLReaderImpl wsdlReader = new WSDLReaderImpl(wsdlvalinfo); + wsdlReader.setMessageGenerator(messagegenerator); + if(domModel != null) + { + wsdlDocs = wsdlReader.readWSDL(file, domModel); + } + else + { + wsdlDocs = wsdlReader.readWSDL(file); + } + //wsdlvalinfo.setElementLocations(wsdlReader.getElementLocationsHashtable()); +// List readerErrors = wsdlReader.getReaderErrors(); +// if (readerErrors != null) +// { +// Iterator readerErrorsI = readerErrors.iterator(); +// while (readerErrorsI.hasNext()) +// { +// ReaderError re = (ReaderError)readerErrorsI.next(); +// wsdlvalinfo.addError(re.getError(), re.getObject()); +// } +// } +// if (wsdlReader.hasImportSchemas()) +// { +// List xsdList = wsdlReader.getImportSchemas(); +// Iterator xsdIter = xsdList.iterator(); +// while (xsdIter.hasNext()) +// { +// wsdlvalinfo.addSchema((XSModel)xsdIter.next()); +// } +// +// } + + } + catch (WSDLException e) + { + throw new ValidateWSDLException(e.getMessage() + " " + e.getFaultCode()); + } + + catch (Exception e) + { + throw new ValidateWSDLException("unable to read file" + e.getMessage() + " " + e.toString()); + } + return wsdlDocs; + } + + /** + * Given a WSDL element, call ValidateElement for it. + * + * @param namespace The namespace of the element to validate. + * @param element The element to validate. + * @param parents The list of parents for this element. + */ + public void validateWSDLElement(String namespace, Object element, List parents, IWSDL11ValidationInfo wsdlvalinfo) + { + IWSDL11Validator val = ver.queryValidatorRegistry(namespace); + if (val != null) + { + val.validate(element, parents, wsdlvalinfo); + } + else + { + //TODO: Add this as a preference. + //wsdlvalinfo.addWarning(messagegenerator.getString(_WARN_NO_VALDIATOR, namespace), element); + } + } + + /** + * Add a schema to the list of schemas. + * + * @param xsModel The schema to add. + */ +// public void addSchema(XSModel xsModel) +// { +// if (xsModel != null) +// { +// schemas.add(xsModel); +// } +// } + + /** + * Return the list containing the schemas. + * + * @return The list of schemas. + */ +// public List getSchemas() +// { +// return schemas; +// } + + /** + * Get the ResourceBundle for this ValidatorManager. + * + * @return The resource bundle registered for this controller. + * @see #setResourceBundle + */ +// public ResourceBundle getResourceBundle() +// { +// return resourcebundle; +// } + + /** + * Set the ResourceBundle for this ValidatorManager. + * + * @param rb The resource bundle to set. + * @see #getResourceBundle + */ + public void setResourceBundle(ResourceBundle rb) + { + if (messagegenerator == null) + { + messagegenerator = new MessageGenerator(rb); + } + + } + + /** + * Set the message generator for this controller. + * + * @param mesgen The message generator to set for this controller. + */ + public void setMessageGenerator(MessageGenerator mesgen) + { + messagegenerator = mesgen; + } + + /** + * Get the message generator registered for this controller. + * + * @return The message generator registered for this controller. + */ + public MessageGenerator getMessageGenerator() + { + return messagegenerator; + } + + /** + * Return the filename for the file currently being validated. Some validators require this. + * + * @return The filename for the file being validated. + */ +// public String getFilename() +// { +// return fileURI; +// } + + /** + * Convenience method for extensibly validators to add error messages. + * + * @param object The object to add the error for. + * @param error The error to add. + */ +// public void addErrorMessage(Object object, String error) +// { +// addValidationMessage(ERROR_MESSAGE, object, error); +// errors = true; +// } + + /** + * Method for extensibly validators to add error messages when they know + * line and column numbers. + * + * @param line The line where the error message is located. + * @param column The column where the error message is located. + * @param error The error message. + */ +// public void addErrorMessage(int line, int column, String error) +// { +// addValidationMessage(ERROR_MESSAGE, line, column, error); +// errors = true; +// } + + /** + * Convenience method for extensibly validators to add warning messages. + * + * @param object The object to add the warning message. + * @param warning The warning message. + */ +// public void addWarningMessage(Object object, String warning) +// { +// addValidationMessage(WARNING_MESSAGE, object, warning); +// } + + /** + * Method for extensibly validators to add warning messages when they know + * line and column numbers. + * + * @param line The line where the error message is located. + * @param column The column where the error message is located. + * @param warning The warning message. + */ +// public void addWarningMessage(int line, int column, String warning) +// { +// addValidationMessage(WARNING_MESSAGE, line, column, warning); +// } + + /** + * If you have an object read in by the reader for this + * validatorcontroller the object can be passed in here and the line and column + * information will be abstracted from it. + * + * @param type The type of message to add. + * @param o The object that has the error (used to get the location). + * @param message The message to add. + */ +// protected void addValidationMessage(int type, Object o, String message) +// { +// int[] location; +// if (elementLocations.containsKey(o)) +// { +// location = (int[])elementLocations.get(o); +// } +// // if we give it an element that hasn't been defined we'll set the location +// // at (0,0) so the error shows up but no line marker in the editor +// else +// { +// location = new int[] { 0, 0 }; +// } +// addValidationMessage(type, location[0], location[1], message); +// } + + /** + * Creates a validation message of the specified type. + * + * @param type The type of validation message to add. + * @param line The line where the error message is located. + * @param column The line where the column message is located. + * @param message The message to add. + */ +// protected void addValidationMessage(int type, int line, int column, String message) +// { +// if (message != null) +// { +// if (type == ERROR_MESSAGE) +// { +// validationController.addErrorMessage(line, column, message); +// } +// else if (type == WARNING_MESSAGE) +// { +// validationController.addWarningMessage(line, column, message); +// } +// } +// } + + /** + * @see org.eclipse.wsdl.validate.controller.IWSDLValidator#isValid() + */ +// public boolean isValid() +// { +// return !errors; +// } + + /** + * Reset the validator controller. + */ +// protected void reset() +// { +// schemas = new Vector(); +// fileURI = ""; +// wsdlDefinition = null; +// elementLocations = null; +// resourcebundle = null; +// //validationController = null; +// errors = false; +// } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11ValidatorDelegate.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11ValidatorDelegate.java new file mode 100644 index 000000000..e627a7812 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDL11ValidatorDelegate.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +import java.util.Locale; +import java.util.ResourceBundle; + + +/** + * The WSDL 1.1 validator delegate holds a reference to a validator to be instantiated at + * a later point. + */ +public class WSDL11ValidatorDelegate +{ + private String validatorClassname = null; + private String resourceBundle = null; + private ClassLoader classLoader = null; + private IWSDL11Validator validator = null; + + /** + * Create a delegate for a validator by its class name and resource bundle name. + * + * @param validatorClassname The name of the validator class. + * @param resourceBundle The name of the validator base resource bundle. + */ + public WSDL11ValidatorDelegate(String validatorClassname, String resourceBundle) + { + this.validatorClassname = validatorClassname; + this.resourceBundle = resourceBundle; + } + + /** + * Create a delegate for a validator by its class name, resource bundle name and + * a class loader to load the validator and bundle. + * + * @param validatorClassname The name of the validator class. + * @param resourceBundle The name of the validator base resource bundle. + * @param classLoader The class loader to use to load the validator and bundle. + */ + public WSDL11ValidatorDelegate(String validatorClassname, String resourceBundle, ClassLoader classLoader) + { + this(validatorClassname, resourceBundle); + this.classLoader = classLoader; + } + + /** + * Get the validator specified in this delegate. + * + * @return The WSDL 1.1 validator specified by this delegate. + */ + public IWSDL11Validator getValidator() + { + if (validator == null) + { + if(classLoader == null) + { + classLoader = getClass().getClassLoader(); + } + try + { + Class validatorClass = + classLoader != null ? classLoader.loadClass(validatorClassname) : Class.forName(validatorClassname); + + validator = (IWSDL11Validator)validatorClass.newInstance(); + if (resourceBundle != null) + { + ResourceBundle validatorBundle = ResourceBundle.getBundle(resourceBundle, Locale.getDefault(), classLoader); + validator.setResourceBundle(validatorBundle); + } + } + catch (ClassNotFoundException e) + { + // TODO: add logging + System.err.println(e); + } + catch (IllegalAccessException e) + { + // TODO: add logging + System.err.println(e); + } + catch (InstantiationException e) + { + // TODO: add logging + System.err.println(e); + } + } + return validator; + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDLDocument.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDLDocument.java new file mode 100644 index 000000000..ebb113182 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDLDocument.java @@ -0,0 +1,2000 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; + +import javax.wsdl.Binding; +import javax.wsdl.BindingFault; +import javax.wsdl.BindingInput; +import javax.wsdl.BindingOperation; +import javax.wsdl.BindingOutput; +import javax.wsdl.Definition; +import javax.wsdl.Fault; +import javax.wsdl.Input; +import javax.wsdl.Message; +import javax.wsdl.Operation; +import javax.wsdl.OperationType; +import javax.wsdl.Output; +import javax.wsdl.Part; +import javax.wsdl.Port; +import javax.wsdl.PortType; +import javax.wsdl.Service; +import javax.wsdl.Types; +import javax.wsdl.WSDLException; +import javax.wsdl.extensions.ExtensibilityElement; +import javax.wsdl.extensions.ExtensionDeserializer; +import javax.wsdl.extensions.ExtensionRegistry; +import javax.wsdl.extensions.UnknownExtensibilityElement; +import javax.wsdl.factory.WSDLFactory; +import javax.xml.namespace.QName; + +import org.apache.xerces.dom.ElementImpl; +import org.apache.xerces.dom.ElementNSImpl; +import org.apache.xerces.xs.XSModel; +import org.eclipse.wst.wsdl.validation.internal.util.MessageGenerator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd.InlineSchemaValidator; +import org.eclipse.wst.wsdl.validation.internal.xml.ElementLocation; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import com.ibm.wsdl.Constants; +import com.ibm.wsdl.util.StringUtils; +import com.ibm.wsdl.util.xml.DOMUtils; +import com.ibm.wsdl.util.xml.QNameUtils; +import com.ibm.wsdl.util.xml.XPathUtils; + +/** + * A WSDL document that knows how to parse itself. + */ +public class WSDLDocument +{ + private static final List STYLE_ONE_WAY = Arrays.asList(new String[] { Constants.ELEM_INPUT }); + private static final List STYLE_REQUEST_RESPONSE = + Arrays.asList(new String[] { Constants.ELEM_INPUT, Constants.ELEM_OUTPUT }); + private static final List STYLE_SOLICIT_RESPONSE = + Arrays.asList(new String[] { Constants.ELEM_OUTPUT, Constants.ELEM_INPUT }); + private static final List STYLE_NOTIFICATION = Arrays.asList(new String[] { Constants.ELEM_OUTPUT }); + + private static final String _ERROR_MULTIPLE_TYPES_DEFINED = "_ERROR_MULTIPLE_TYPES_DEFINED"; + private static final String _UNABLE_TO_IMPORT_NO_LOCATION = "_UNABLE_TO_IMPORT_NO_LOCATION"; + + protected ExtensionRegistry extReg = null; + protected String factoryImplName = null; + + // store the element locations within the file - line and column numbers + // the location info is stored as an int array of length 2 {linenumber, colnumber} + protected Hashtable elementLocations = new Hashtable(); + + // hold the reader errors + protected List readerErrors = new ArrayList(); + protected List readerWarnings = new ArrayList(); + protected MessageGenerator messagegenerator; + + private Definition def = null; + private Set importedDefs = new TreeSet(); + private Element typesEl = null; + private List messages = new ArrayList(); + private List porttypes = new ArrayList(); + private List bindings = new ArrayList(); + private List services = new ArrayList(); + private List extelements = new ArrayList(); + private int depth; + // Hold the schemas that are imported or declared inline in this wsdl document. + private List schemas = new ArrayList(); + private IWSDL11ValidationInfo valinfo; + + /** + * Constructor. Performs a preparse of the document and handles imports and types. + * + * @param documentBaseURI The URI of this WSDL document. + * @param defEl The definitions element. + * @param depth The depth of this document in a document tree. + * @param messagegenerator A messagegenerator used for retrieving strings. + * @param valinfo A WSDL11ValidationInfo object for reporting messages. + * @throws WSDLException + */ + public WSDLDocument(String documentBaseURI, Element defEl, int depth, MessageGenerator messagegenerator, IWSDL11ValidationInfo valinfo) throws WSDLException + { + this.messagegenerator = messagegenerator; + this.valinfo = valinfo; + this.depth = depth; + + checkElementName(defEl, Constants.Q_ELEM_DEFINITIONS); + + WSDLFactory factory = + (factoryImplName != null) ? WSDLFactory.newInstance(factoryImplName) : WSDLFactory.newInstance(); + def = factory.newDefinition(); + + if (extReg != null) + { + def.setExtensionRegistry(extReg); + } + + String name = DOMUtils.getAttribute(defEl, Constants.ATTR_NAME); + String targetNamespace = DOMUtils.getAttribute(defEl, Constants.ATTR_TARGET_NAMESPACE); + NamedNodeMap attrs = defEl.getAttributes(); + + if (documentBaseURI != null) + { + def.setDocumentBaseURI(documentBaseURI); + } + + if (name != null) + { + def.setQName(new QName(targetNamespace, name)); + } + + if (targetNamespace != null) + { + def.setTargetNamespace(targetNamespace); + } + + int size = attrs.getLength(); + + for (int i = 0; i < size; i++) + { + Attr attr = (Attr)attrs.item(i); + String namespaceURI = attr.getNamespaceURI(); + String localPart = attr.getLocalName(); + String value = attr.getValue(); + + if (namespaceURI != null && namespaceURI.equals(Constants.NS_URI_XMLNS)) + { + if (localPart != null && !localPart.equals(Constants.ATTR_XMLNS)) + { + def.addNamespace(localPart, value); + } + else + { + def.addNamespace(null, value); + } + } + } + + // There are problems when the model is created and the order of elements is not + // Import - Types - Message - PortType - Binding - Service so we need to read them + // into the model in that order + // Performance wise this should be modified as we have to make 5 extra loops through + // all of the elements as a result + + // take care of Imports, Types, Documentation and extensibleelements together - saves a pass for Documentation + // later and Docs don't effect anything else - Imports and Types are essentially the same thing + // no preconceived ideas about extensible elements + Element tempEl = DOMUtils.getFirstChildElement(defEl); + + while (tempEl != null) + { + + if (QNameUtils.matches(Constants.Q_ELEM_IMPORT, tempEl)) + { + String namespaceURI = DOMUtils.getAttribute(tempEl, Constants.ATTR_NAMESPACE); + String locationURI = DOMUtils.getAttribute(tempEl, Constants.ATTR_LOCATION); + if(locationURI == null || locationURI.equals("")) + { + addReaderError(def, tempEl, messagegenerator.getString(_UNABLE_TO_IMPORT_NO_LOCATION)); + } + else + { + ImportHolder ih = new ImportHolder(namespaceURI, locationURI, def.getDocumentBaseURI(), this, depth+1, tempEl, messagegenerator, valinfo); + importedDefs.add(ih); + } + setLocation(tempEl, tempEl); +// if (importedDefs == null) +// { +// importedDefs = new Hashtable(); +// } +// if (documentBaseURI != null) +// { +// importedDefs.put(documentBaseURI, def); +// } +// def.addImport(parseImport(tempEl, def, importedDefs)); + } + else if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + def.setDocumentationElement(tempEl); + } + else if (QNameUtils.matches(Constants.Q_ELEM_TYPES, tempEl)) + { + if(typesEl != null) + { + setLocation(tempEl, tempEl); + addReaderError(def, tempEl, messagegenerator.getString(_ERROR_MULTIPLE_TYPES_DEFINED)); + } + else + { + typesEl = tempEl; + parseTypes(); + } +// def.setTypes(parseTypes(tempEl, def)); + } + else if (QNameUtils.matches(Constants.Q_ELEM_MESSAGE, tempEl)) + { + messages.add(tempEl); + //def.addMessage(parseMessage(tempEl, def)); + } + else if (QNameUtils.matches(Constants.Q_ELEM_PORT_TYPE, tempEl)) + { + porttypes.add(tempEl); + // PortType pt = parsePortType(tempEl, def); + // if(pt != null) + // { + // def.addPortType(pt); + // } + } + else if (QNameUtils.matches(Constants.Q_ELEM_BINDING, tempEl)) + { + bindings.add(tempEl); + //def.addBinding(parseBinding(tempEl, def)); + } + else if (QNameUtils.matches(Constants.Q_ELEM_SERVICE, tempEl)) + { + services.add(tempEl); + //def.addService(parseService(tempEl, def)); + } + else + { + extelements.add(tempEl); + // def.addExtensibilityElement( + // parseExtensibilityElement(Definition.class, tempEl, def)); + } + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + } + + /** + * Get the definitions element for this document. + * + * @return The definitions element for this document. + */ + public Definition getDefinition() + { + return def; + } + + /** + * Get a set of the imports in this document. + * + * @return A set of the imports in this document. + */ + public Set getImports() + { + return importedDefs; + } + + /** + * + * @param def + * @param doc + * @param namespace + * @param location + */ +// public void setImport(Definition def, Element doc, String namespace, String location) +// { +// if(location == null || location.equals("")) +// { +// valinfo.addError()_UNABLE_TO_IMPORT_NO_LOCATION +// } +// Import imp = def.createImport(); +// imp.setDefinition(def); +// imp.setDocumentationElement(doc); +// imp.setNamespaceURI(namespace); +// imp.setLocationURI(location); +// def.addImport(imp); +// } + + /** + * Parse the types in the WSDL document. Handles documentation, import and schema + * elements. + */ + public void parseTypes() + { + Types types = def.createTypes(); + + Element tempEl = DOMUtils.getFirstChildElement(typesEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + types.setDocumentationElement(tempEl); + } + else if (tempEl.getLocalName().equalsIgnoreCase("import")) + //else if (QNameUtils.matches(Constants.Q_ELEM_IMPORT, tempEl)) + { + // this shouldn't really be used here but a little hack will make + // life easier + //parseImport(tempEl, def, (Map)new Hashtable()); + String namespaceURI = DOMUtils.getAttribute(tempEl, Constants.ATTR_NAMESPACE); + String locationURI = DOMUtils.getAttribute(tempEl, "schemaLocation"); + importedDefs.add(new ImportHolder(namespaceURI, locationURI, def.getDocumentBaseURI(), this, depth+1, tempEl, messagegenerator, valinfo)); + try + { + types.addExtensibilityElement(parseExtensibilityElement(Types.class, tempEl, def)); + } + catch(WSDLException e) + { + + } + } + else + { + try + { + ExtensibilityElement extElem = parseExtensibilityElement(Types.class, tempEl, def); + types.addExtensibilityElement(extElem); + } + catch(WSDLException e) + { + + } + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + def.setTypes(types); + + valinfo.setElementLocations(elementLocations); + List typesElems = types.getExtensibilityElements(); + if(typesElems != null) + { + Iterator typesElemsIter = typesElems.iterator(); + while(typesElemsIter.hasNext()) + { + ExtensibilityElement typeElement = (ExtensibilityElement)typesElemsIter.next(); + + InlineSchemaValidator xsdVal = new InlineSchemaValidator(); + xsdVal.setMessageGenerator(messagegenerator); + + List parents = new ArrayList(); + parents.add(def); + parents.add(0,types); + xsdVal.validate(typeElement, parents,valinfo); + XSModel[] typesSchemas = valinfo.getSchemas(); + List typesSchemaList = new ArrayList(); + for(int i = 0; i < typesSchemas.length; i++) + { + typesSchemaList.add(typesSchemas[i]); + } + schemas.addAll(typesSchemaList); + valinfo.clearSchemas(); + } + } + valinfo.setElementLocations(null); + } + + /** + * Parse the messages in this document. + */ + public void parseMessages() + { + for (int i = 0; i < messages.size(); i++) + { + try + { + def.addMessage(parseMessage((Element)messages.get(i), def)); + } + catch(WSDLException e) + {} + } + } + + /** + * Parse the portTypes in this document. + */ + public void parsePorttypes() + { + for (int i = 0; i < porttypes.size(); i++) + { + try + { + PortType pt = parsePortType((Element)porttypes.get(i), def); + if (pt != null) + { + def.addPortType(pt); + } + } + catch(WSDLException e) + {} + } + } + + /** + * Parse the bindings in this document. + */ + public void parseBindings() + { + for (int i = 0; i < bindings.size(); i++) + { + try + { + def.addBinding(parseBinding((Element)bindings.get(i), def)); + } + catch(WSDLException e) + {} + } + } + + /** + * Parse the services in this document. + */ + public void parseServices() + { + for (int i = 0; i < services.size(); i++) + { + try + { + def.addService(parseService((Element)services.get(i), def)); + } + catch(WSDLException e) + {} + } + } + + /** + * Parse the extensibility elements in this document. + */ + public void parseExtensibilityElements() + { + for (int i = 0; i < extelements.size(); i++) + { + try + { + def.addExtensibilityElement(parseExtensibilityElement(Definition.class, (Element)extelements.get(i), def)); + } + catch(WSDLException e) + {} + } + } + + /** + * Add the given list of schemas to the schemas for this document. + * + * @param schemas The list of schemas to add to this document's schemas. + */ + public void addSchemas(List schemas) + { + this.schemas.addAll(schemas); + } + + /** + * Get the schemas associated with this document. + * + * @return The schemas associated with this document. + */ + public List getSchemas() + { + return schemas; + } + + /** + * Parse the specified binding. + * + * @param bindingEl The binding element. + * @param def The definitions element. + * @return A WSDL binding element. + * @throws WSDLException + */ + protected Binding parseBinding(Element bindingEl, Definition def) throws WSDLException + { + Binding binding = null; + String name = DOMUtils.getAttribute(bindingEl, Constants.ATTR_NAME); + QName portTypeName; + try + { + portTypeName = DOMUtils.getQualifiedAttributeValue(bindingEl, Constants.ATTR_TYPE, Constants.ELEM_BINDING, false); + } + catch (Exception e) + { + //the call above fails if there is no qualified namespace for the message name + portTypeName = new QName(null, DOMUtils.getAttribute(bindingEl, "type")); + } + + PortType portType = null; + + if (name != null) + { + QName bindingName = new QName(def.getTargetNamespace(), name); + + binding = def.getBinding(bindingName); + + if (binding == null) + { + binding = def.createBinding(); + binding.setQName(bindingName); + } + // report an error if a binding with this name has already been defined + else if (!binding.isUndefined()) + { + //addReaderError(def,bindingEl, "_BINDING_NAME_ALREADY_DEFINED"); + addReaderError( + def, + bindingEl, + messagegenerator.getString("_BINDING_NAME_ALREADY_DEFINED", "'" + binding.getQName().getLocalPart() + "'")); + } + } + else + { + binding = def.createBinding(); + } + + // Whether it was retrieved or created, the definition has been found. + binding.setUndefined(false); + + if (portTypeName != null) + { + portType = def.getPortType(portTypeName); + + if (portType == null) + { + portType = def.createPortType(); + portType.setQName(portTypeName); + def.addPortType(portType); + } + + binding.setPortType(portType); + } + + Element tempEl = DOMUtils.getFirstChildElement(bindingEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + binding.setDocumentationElement(tempEl); + } + else if (QNameUtils.matches(Constants.Q_ELEM_OPERATION, tempEl)) + { + binding.addBindingOperation(parseBindingOperation(tempEl, portType, def)); + } + else + { + binding.addExtensibilityElement(parseExtensibilityElement(Binding.class, tempEl, def)); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(binding, bindingEl); + + return binding; + } + + /** + * Parse a specific binding operation. + * + * @param bindingOperationEl The binding operation element. + * @param portType The portType the binding references. + * @param def The definitions element. + * @return A WSDL binding operation element. + * @throws WSDLException + */ + protected BindingOperation parseBindingOperation(Element bindingOperationEl, PortType portType, Definition def) + throws WSDLException + { + BindingOperation bindingOperation = def.createBindingOperation(); + String name = DOMUtils.getAttribute(bindingOperationEl, Constants.ATTR_NAME); + + if (name != null) + { + bindingOperation.setName(name); + } + + Element tempEl = DOMUtils.getFirstChildElement(bindingOperationEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + bindingOperation.setDocumentationElement(tempEl); + } + else if (QNameUtils.matches(Constants.Q_ELEM_INPUT, tempEl)) + { + bindingOperation.setBindingInput(parseBindingInput(tempEl, def)); + } + else if (QNameUtils.matches(Constants.Q_ELEM_OUTPUT, tempEl)) + { + bindingOperation.setBindingOutput(parseBindingOutput(tempEl, def)); + } + else if (QNameUtils.matches(Constants.Q_ELEM_FAULT, tempEl)) + { + bindingOperation.addBindingFault(parseBindingFault(tempEl, def)); + } + else + { + bindingOperation.addExtensibilityElement(parseExtensibilityElement(BindingOperation.class, tempEl, def)); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + if (portType != null) + { + BindingInput bindingInput = bindingOperation.getBindingInput(); + BindingOutput bindingOutput = bindingOperation.getBindingOutput(); + String inputName = (bindingInput != null ? bindingInput.getName() : null); + String outputName = (bindingOutput != null ? bindingOutput.getName() : null); + + // hack code to get at operations that are defined with the same name but different + // inputs and outputs + Operation op = null; + List operations = portType.getOperations(); + // get a list of all operations with matching names + List matchingOperations = new Vector(); + Iterator iOperations = operations.iterator(); + while (iOperations.hasNext()) + { + Operation oper = (Operation)iOperations.next(); + if (oper.getName().equalsIgnoreCase(bindingOperation.getName())) + { + matchingOperations.add(oper); + } + } + + if (matchingOperations != null) + { + // If there's only one matching operation this is what we're referring to. + // Only matching if binding operation input name and output name are + // both null or the same as the portType operation input and output names. + // the portType operation name + if (matchingOperations.size() == 1) + { + // only say the single operation is the one we're looking for if the names + // of the binding input and output are not specified + Operation tempOp = (Operation)matchingOperations.get(0); + boolean inputOK = false; + boolean outputOK = false; + Input tempInput = tempOp.getInput(); + Output tempOutput = tempOp.getOutput(); + + // order is important in these conditions. condition 2 must fail for 3 to work + // check the input + if (tempInput == null && bindingInput == null) + { + inputOK = true; + } + else if (bindingInput == null || bindingInput.getName() == null) + { + inputOK = true; + } + else if (tempInput != null && bindingInput.getName().equals(tempInput.getName())) + { + inputOK = true; + } + // check the output + if (tempOutput == null && bindingOutput == null) + { + outputOK = true; + } + else if (bindingOutput == null || bindingOutput.getName() == null) + { + outputOK = true; + } + else if (tempOutput != null && bindingOutput.getName().equals(tempOutput.getName())) + { + outputOK = true; + } + if (inputOK && outputOK) + { + op = tempOp; + } + // op = (Operation) matchingOperations.get(0); + } + // otherwise find the operation with the same name, inputname, outputname signature + if (matchingOperations != null && op == null) + { + Iterator iMatchingOperations = matchingOperations.iterator(); + while (iMatchingOperations.hasNext()) + { + + boolean inputNamesEqual = false; + boolean outputNamesEqual = false; + Operation oper = (Operation)iMatchingOperations.next(); + // if (oper.getName().equalsIgnoreCase(bindingOperation.getName())) + // { + Input opInput = oper.getInput(); + if (opInput != null && bindingInput != null) + { + String opInputName = opInput.getName(); + String bindingInputName = bindingInput.getName(); + if (opInputName != null && opInputName.equalsIgnoreCase(bindingInputName)) + { + inputNamesEqual = true; + } + else if (opInputName == null && bindingInputName == null) + { + inputNamesEqual = true; + } + } + else if (opInput == null && bindingInput == null) + { + inputNamesEqual = true; + } + Output opOutput = oper.getOutput(); + if (opOutput != null && bindingOutput != null) + { + String opOutputName = opOutput.getName(); + String bindingOutputName = bindingOutput.getName(); + if (opOutputName != null && opOutputName.equalsIgnoreCase(bindingOutputName)) + { + outputNamesEqual = true; + } + else if (opOutputName == null && bindingOutputName == null) + { + outputNamesEqual = true; + } + } + else if (opOutput == null && bindingOutput == null) + { + outputNamesEqual = true; + } + if (inputNamesEqual && outputNamesEqual) + { + op = oper; + break; + } + //} + } + } + } + //Operation op = portType.getOperation(name, inputName, outputName); + + if (op == null) + { + op = def.createOperation(); + op.setName(name); + portType.addOperation(op); + } + + bindingOperation.setOperation(op); + } + + // add the location of this element to elementLocations + setLocation(bindingOperation, bindingOperationEl); + + return bindingOperation; + } + + /** + * Parse a specific binding input element. + * + * @param bindingInputEl The binding input element. + * @param def The definitions element. + * @return A WSDL binding input element. + * @throws WSDLException + */ + protected BindingInput parseBindingInput(Element bindingInputEl, Definition def) throws WSDLException + { + BindingInput bindingInput = def.createBindingInput(); + String name = DOMUtils.getAttribute(bindingInputEl, Constants.ATTR_NAME); + + if (name != null) + { + bindingInput.setName(name); + } + + Element tempEl = DOMUtils.getFirstChildElement(bindingInputEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + bindingInput.setDocumentationElement(tempEl); + } + else + { + bindingInput.addExtensibilityElement(parseExtensibilityElement(BindingInput.class, tempEl, def)); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(bindingInput, bindingInputEl); + + return bindingInput; + } + + /** + * Parse a specific binding output element. + * + * @param bindingOutputEl The binding output element. + * @param def The definitions element. + * @return A WSDL binding output element. + * @throws WSDLException + */ + protected BindingOutput parseBindingOutput(Element bindingOutputEl, Definition def) throws WSDLException + { + BindingOutput bindingOutput = def.createBindingOutput(); + String name = DOMUtils.getAttribute(bindingOutputEl, Constants.ATTR_NAME); + + if (name != null) + { + bindingOutput.setName(name); + } + + Element tempEl = DOMUtils.getFirstChildElement(bindingOutputEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + bindingOutput.setDocumentationElement(tempEl); + } + else + { + bindingOutput.addExtensibilityElement(parseExtensibilityElement(BindingOutput.class, tempEl, def)); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(bindingOutput, bindingOutputEl); + + return bindingOutput; + } + + /** + * Parse a specific binding fault element. + * + * @param bindingFaultEl The binding fault element. + * @param def The definitions element. + * @return A WSDL binding fault element. + * @throws WSDLException + */ + protected BindingFault parseBindingFault(Element bindingFaultEl, Definition def) throws WSDLException + { + BindingFault bindingFault = def.createBindingFault(); + String name = DOMUtils.getAttribute(bindingFaultEl, Constants.ATTR_NAME); + + if (name != null) + { + bindingFault.setName(name); + } + + Element tempEl = DOMUtils.getFirstChildElement(bindingFaultEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + bindingFault.setDocumentationElement(tempEl); + } + else + { + bindingFault.addExtensibilityElement(parseExtensibilityElement(BindingFault.class, tempEl, def)); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(bindingFault, bindingFaultEl); + + return bindingFault; + } + + /** + * Parse a specific message element. + * + * @param msgEl The message element. + * @param def The definitions element. + * @return A WSDL message element. + * @throws WSDLException + */ + protected Message parseMessage(Element msgEl, Definition def) throws WSDLException + { + Message msg = null; + String name = DOMUtils.getAttribute(msgEl, Constants.ATTR_NAME); + + if (name != null) + { + QName messageName = new QName(def.getTargetNamespace(), name); + + msg = def.getMessage(messageName); + + if (msg == null) + { + msg = def.createMessage(); + msg.setQName(messageName); + } + else if (!msg.isUndefined()) + { + // produce an error message as a message with this name has already been defined + addReaderError( + def, + msgEl, + messagegenerator.getString("_MESSAGE_NAME_ALREADY_DEFINED", "'" + msg.getQName().getLocalPart() + "'")); + } + } + else + { + msg = def.createMessage(); + } + + // Whether it was retrieved or created, the definition has been found. + msg.setUndefined(false); + + Element tempEl = DOMUtils.getFirstChildElement(msgEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + msg.setDocumentationElement(tempEl); + } + else if (QNameUtils.matches(Constants.Q_ELEM_PART, tempEl)) + { + msg.addPart(parsePart(tempEl, def)); + } + else + { + // XML Validation will catch this + DOMUtils.throwWSDLException(tempEl); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(msg, msgEl); + + return msg; + } + + /** + * Parse a specific part element. + * + * @param partEl The part element. + * @param def The definitions element. + * @return A WSDL part element. + * @throws WSDLException + */ + protected Part parsePart(Element partEl, Definition def) throws WSDLException + { + Part part = def.createPart(); + String name = DOMUtils.getAttribute(partEl, Constants.ATTR_NAME); + + QName elementName; + try + { + elementName = DOMUtils.getQualifiedAttributeValue(partEl, Constants.ATTR_ELEMENT, Constants.ELEM_MESSAGE, false); + } + catch (Exception e) + { + //the call above fails if there is no qualified namespace for the element name + elementName = new QName(null, DOMUtils.getAttribute(partEl, "element")); + } + + QName typeName; + try + { + typeName = DOMUtils.getQualifiedAttributeValue(partEl, Constants.ATTR_TYPE, + // Corrected - was ATTR_ELEMENT + Constants.ELEM_MESSAGE, false); + } + catch (Exception e) + { + //the call above fails if there is no qualified namespace for the element attribute + typeName = new QName(null, DOMUtils.getAttribute(partEl, "name")); + } + + if (name != null) + { + part.setName(name); + } + + if (elementName != null) + { + part.setElementName(elementName); + } + + if (typeName != null) + { + part.setTypeName(typeName); + } + + Element tempEl = DOMUtils.getFirstChildElement(partEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + part.setDocumentationElement(tempEl); + } + else + { + // XML Validation will catch this + DOMUtils.throwWSDLException(tempEl); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + Map extensionAttributes = part.getExtensionAttributes(); + + extensionAttributes.putAll(getPartAttributes(partEl, def)); + + // Need to do something here to locate part definition. + + // add the location of this element to elementLocations + setLocation(part, partEl); + + return part; + } + + /** + * Get a map of the part attributes. + * + * @param el The part attributes element. + * @param def The defintions element. + * @return A map containing the part attributes. + * @throws WSDLException + */ + protected Map getPartAttributes(Element el, Definition def) throws WSDLException + { + Map attributes = new HashMap(); + NamedNodeMap nodeMap = el.getAttributes(); + int atts = nodeMap.getLength(); + + for (int a = 0; a < atts; a++) + { + Attr attribute = (Attr)nodeMap.item(a); + String lName = attribute.getLocalName(); + String nSpace = attribute.getNamespaceURI(); + String prefix = attribute.getPrefix(); + QName name = new QName(nSpace, lName); + + if (nSpace != null && !nSpace.equals(Constants.NS_URI_WSDL)) + { + if (!nSpace.equals(Constants.NS_URI_XMLNS)) + { + String strValue = attribute.getValue(); + QName qValue = null; + + try + { + qValue = DOMUtils.getQName(strValue, el); + } + catch (WSDLException e) + { + qValue = new QName(strValue); + } + + attributes.put(name, qValue); + + String tempNSUri = def.getNamespace(prefix); + + while (tempNSUri != null && !tempNSUri.equals(nSpace)) + { + prefix += "_"; + tempNSUri = def.getNamespace(prefix); + } + + def.addNamespace(prefix, nSpace); + } + } + else if ( + !lName.equals(Constants.ATTR_NAME) + && !lName.equals(Constants.ATTR_ELEMENT) + && !lName.equals(Constants.ATTR_TYPE)) + { + WSDLException wsdlExc = + new WSDLException( + WSDLException.INVALID_WSDL, + "Encountered illegal " + + "part extension " + + "attribute '" + + name + + "'. Extension " + + "attributes must be in " + + "a namespace other than " + + "WSDL's."); + + wsdlExc.setLocation(XPathUtils.getXPathExprFromNode(el)); + //throw wsdlExc; + } + } + + // add the location of this element to elementLocations + setLocation(attributes, el); + + return attributes; + } + + /** + * Parse a specific portType element. + * + * @param portTypeEl The portType element. + * @param def The defintions element. + * @return A WSDL portType element. + * @throws WSDLException + */ + protected PortType parsePortType(Element portTypeEl, Definition def) throws WSDLException + { + + PortType portType = null; + String name = DOMUtils.getAttribute(portTypeEl, Constants.ATTR_NAME); + + if (name != null) + { + QName portTypeName = new QName(def.getTargetNamespace(), name); + + portType = def.getPortType(portTypeName); + + if (portType == null) + { + portType = def.createPortType(); + portType.setQName(portTypeName); + } + else if (!portType.isUndefined()) + { + // if the PortType has already been defined produce an error and return null + addReaderError( + def, + portTypeEl, + messagegenerator.getString("_PORTTYPE_NAME_ALREADY_DEFINED", "'" + portType.getQName().getLocalPart() + "'")); + return null; + } + } + else + { + portType = def.createPortType(); + } + + // Whether it was retrieved or created, the definition has been found. + portType.setUndefined(false); + + Element tempEl = DOMUtils.getFirstChildElement(portTypeEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + portType.setDocumentationElement(tempEl); + } + else if (QNameUtils.matches(Constants.Q_ELEM_OPERATION, tempEl)) + { + // modified so duplicate operations will not be added to porttype + Operation op = parseOperation(tempEl, portType, def); + if (op != null) + { + portType.addOperation(op); + } + //portType.addOperation(parseOperation(tempEl, portType, def)); + } + else + { + // something else that shouldn't be here + // NEED TO ADD TO ERROR LIST + //DOMUtils.throwWSDLException(tempEl); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(portType, portTypeEl); + + return portType; + } + + /** + * Parse a specific operation element. + * + * @param opEl The operation element. + * @param portType The portType element. + * @param def The definitions element. + * @return A WSDL operation element. + * @throws WSDLException + */ + protected Operation parseOperation(Element opEl, PortType portType, Definition def) throws WSDLException + { + Operation op = null; + String name = DOMUtils.getAttribute(opEl, Constants.ATTR_NAME); + String parameterOrderStr = DOMUtils.getAttribute(opEl, Constants.ATTR_PARAMETER_ORDER); + Element tempEl = DOMUtils.getFirstChildElement(opEl); + List messageOrder = new Vector(); + Element docEl = null; + Input input = null; + Output output = null; + List faults = new Vector(); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + docEl = tempEl; + } + else if (QNameUtils.matches(Constants.Q_ELEM_INPUT, tempEl)) + { + input = parseInput(tempEl, def); + messageOrder.add(Constants.ELEM_INPUT); + } + else if (QNameUtils.matches(Constants.Q_ELEM_OUTPUT, tempEl)) + { + output = parseOutput(tempEl, def); + messageOrder.add(Constants.ELEM_OUTPUT); + } + else if (QNameUtils.matches(Constants.Q_ELEM_FAULT, tempEl)) + { + faults.add(parseFault(tempEl, def)); + } + else + { + // invalid element in the operation + // XML check will catch this + DOMUtils.throwWSDLException(tempEl); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + if (name != null) + { + String inputName = (input != null ? input.getName() : null); + String outputName = (output != null ? output.getName() : null); + + boolean opDefined = false; + + try + { + + //op = portType.getOperation(name, inputName, outputName); + + //Operation op = null; + List operations = portType.getOperations(); + if (operations != null) + { + + Iterator iOperations = operations.iterator(); + while (iOperations.hasNext()) + { + boolean inputNamesEqual = false; + boolean outputNamesEqual = false; + Operation oper = (Operation)iOperations.next(); + if (oper.getName().equalsIgnoreCase(name)) + { + Input opInput = oper.getInput(); + if (opInput != null && input != null) + { + String opInputName = opInput.getName(); + if (opInputName != null && inputName != null && opInputName.equalsIgnoreCase(inputName)) + { + inputNamesEqual = true; + } + else if (opInputName == null && inputName == null) + { + inputNamesEqual = true; + } + } + else if (opInput == null && input == null) + { + inputNamesEqual = true; + } + Output opOutput = oper.getOutput(); + if (opOutput != null && output != null) + { + String opOutputName = opOutput.getName(); + if (opOutputName != null && outputName != null && opOutputName.equalsIgnoreCase(outputName)) + { + outputNamesEqual = true; + } + else if (opOutputName == null && outputName == null) + { + outputNamesEqual = true; + } + } + else if (opOutput == null && output == null) + { + outputNamesEqual = true; + } + if (inputNamesEqual && outputNamesEqual) + { + op = oper; + break; + } + } + } + } + } + catch (Exception e) + { + opDefined = true; + } + + if (op != null /*&& !op.isUndefined()*/ + ) + { + //op = null; + opDefined = true; + + } + + if (op != null && !opDefined) + { + if (inputName == null) + { + Input tempIn = op.getInput(); + + if (tempIn != null) + { + if (tempIn.getName() != null) + { + //op = null; + opDefined = true; + } + } + } + } + + if (op != null && !opDefined) + { + if (outputName == null) + { + Output tempOut = op.getOutput(); + + if (tempOut != null) + { + if (tempOut.getName() != null) + { + //op = null; + opDefined = true; + } + } + } + } + + if (opDefined) + { + // instead of creating a new one with the same name we're going to return null. + // According to the WSDL 1.2 working draft operation overloading is no longer allowed. + // Going to use that here to save a lot of work + setLocation(op, opEl); + addReaderError( + portType, + op, + messagegenerator.getString( + "_DUPLICATE_OPERATION_FOR_PORTTYPE", + "'" + op.getName() + "'", + "'" + portType.getQName().getLocalPart() + "'")); + return null; + } + if (op == null) + { + op = def.createOperation(); + op.setName(name); + + } + } + else + { + op = def.createOperation(); + } + + // Whether it was retrieved or created, the definition has been found. + op.setUndefined(false); + + if (parameterOrderStr != null) + { + op.setParameterOrdering(StringUtils.parseNMTokens(parameterOrderStr)); + } + + if (docEl != null) + { + op.setDocumentationElement(docEl); + } + + if (input != null) + { + op.setInput(input); + } + + if (output != null) + { + op.setOutput(output); + } + + if (faults.size() > 0) + { + Iterator faultIterator = faults.iterator(); + + while (faultIterator.hasNext()) + { + Fault f = (Fault)faultIterator.next(); + // if the fault isn't defined yet + if (op.getFault(f.getName()) == null) + { + op.addFault(f); + } + else + { + addReaderError( + op, + f, + messagegenerator.getString("_DUPLICATE_FAULT_NAME", "'" + f.getName() + "'", "'" + op.getName() + "'")); + //faultErrors.add(new Object[]{f,"Duplicate Name",op}); + } + } + } + + OperationType style = null; + + if (messageOrder.equals(STYLE_ONE_WAY)) + { + style = OperationType.ONE_WAY; + } + else if (messageOrder.equals(STYLE_REQUEST_RESPONSE)) + { + style = OperationType.REQUEST_RESPONSE; + } + else if (messageOrder.equals(STYLE_SOLICIT_RESPONSE)) + { + style = OperationType.SOLICIT_RESPONSE; + } + else if (messageOrder.equals(STYLE_NOTIFICATION)) + { + style = OperationType.NOTIFICATION; + } + + if (style != null) + { + op.setStyle(style); + } + + // add the location of this element to elementLocations + setLocation(op, opEl); + + // modified to remove duplicate operations + // if(opDefined) + // { + // addReaderError(portType,op,ValidateWSDLPlugin.getInstance().getString("_DUPLICATE_OPERATION_FOR_PORTTYPE","'"+op.getName()+"'","'"+portType.getQName().getLocalPart()+"'")); + // return null; + // } + return op; + } + + /** + * Parse a specific service element. + * + * @param serviceEl The service element. + * @param def The defintions element. + * @return A WSDL service element. + * @throws WSDLException + */ + protected Service parseService(Element serviceEl, Definition def) throws WSDLException + { + Service service = def.createService(); + String name = DOMUtils.getAttribute(serviceEl, Constants.ATTR_NAME); + + if (name != null) + { + service.setQName(new QName(def.getTargetNamespace(), name)); + } + Service s; + // a service with this name has already been defined + if ((s = def.getService(service.getQName())) != null) + { + addReaderError( + def, + serviceEl, + messagegenerator.getString("_SERVICE_NAME_ALREADY_DEFINED", "'" + s.getQName().getLocalPart() + "'")); + return s; + } + Element tempEl = DOMUtils.getFirstChildElement(serviceEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + service.setDocumentationElement(tempEl); + } + else if (QNameUtils.matches(Constants.Q_ELEM_PORT, tempEl)) + { + service.addPort(parsePort(tempEl, def)); + } + else + { + service.addExtensibilityElement(parseExtensibilityElement(Service.class, tempEl, def)); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(service, serviceEl); + + return service; + } + + /** + * Parse a specific port element. + * + * @param portEl The port element. + * @param def The definitions element. + * @return A WSDL port element. + * @throws WSDLException + */ + protected Port parsePort(Element portEl, Definition def) throws WSDLException + { + Port port = def.createPort(); + String name = DOMUtils.getAttribute(portEl, Constants.ATTR_NAME); + QName bindingStr; + try + { + bindingStr = DOMUtils.getQualifiedAttributeValue(portEl, Constants.ATTR_BINDING, Constants.ELEM_PORT, false); + } + catch (Exception e) + { + //the call above fails if there is no qualified namespace for the message name + bindingStr = new QName(null, DOMUtils.getAttribute(portEl, "binding")); + } + + if (name != null) + { + port.setName(name); + } + + if (bindingStr != null) + { + Binding binding = def.getBinding(bindingStr); + + if (binding == null) + { + binding = def.createBinding(); + binding.setQName(bindingStr); + def.addBinding(binding); + } + + port.setBinding(binding); + } + + Element tempEl = DOMUtils.getFirstChildElement(portEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + port.setDocumentationElement(tempEl); + } + else + { + port.addExtensibilityElement(parseExtensibilityElement(Port.class, tempEl, def)); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(port, portEl); + + return port; + } + + /** + * Parse a specific extensibility element. + * + * @param parentType The parent type of the extensibility element. + * @param el The extensibility element. + * @param def The definitions element. + * @return A WSDL extensibility element. + * @throws WSDLException + */ + protected ExtensibilityElement parseExtensibilityElement(Class parentType, Element el, Definition def) + throws WSDLException + { + QName elementType = QNameUtils.newQName(el); + + try + { + ExtensionRegistry extReg = def.getExtensionRegistry(); + + if (extReg == null) + { + throw new WSDLException( + WSDLException.CONFIGURATION_ERROR, + "No ExtensionRegistry set for this " + + "Definition, so unable to deserialize " + + "a '" + + elementType + + "' element in the " + + "context of a '" + + parentType.getName() + + "'."); + } + + ExtensionDeserializer extDS = extReg.queryDeserializer(parentType, elementType); + + // asign the ExtensibilityElement to a var so we can add it to the locations + ExtensibilityElement extElem = extDS.unmarshall(parentType, elementType, el, def, extReg); + // add the location of this element to elementLocations + // this might not work properly + setLocation(extElem, el); + + // register all of the child Elements so we can find them later + // used for inline schema validation + registerChildElements(extElem); + + return extElem; + } + catch (WSDLException e) + { + if (e.getLocation() == null) + { + e.setLocation(XPathUtils.getXPathExprFromNode(el)); + } + throw e; + } + } + + /** + * Parse a specific input element. + * + * @param inputEl The input element. + * @param def The defintions element. + * @return A WSDL input element. + * @throws WSDLException + */ + protected Input parseInput(Element inputEl, Definition def) throws WSDLException + { + + Input input = def.createInput(); + String name = DOMUtils.getAttribute(inputEl, Constants.ATTR_NAME); + QName messageName = null; + try + { + messageName = DOMUtils.getQualifiedAttributeValue(inputEl, Constants.ATTR_MESSAGE, Constants.ELEM_INPUT, false); + } + catch (Exception e) + { + //the call above fails if there is no qualified namespace for the message name + messageName = new QName(null, DOMUtils.getAttribute(inputEl, "message")); + } + + if (name != null) + { + input.setName(name); + } + + if (messageName != null) + { + Message message = def.getMessage(messageName); + + if (message == null) + { + message = def.createMessage(); + message.setQName(messageName); + def.addMessage(message); + } + + input.setMessage(message); + } + + Element tempEl = DOMUtils.getFirstChildElement(inputEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + input.setDocumentationElement(tempEl); + } + else + { + // XML Validation will catch this + DOMUtils.throwWSDLException(tempEl); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(input, inputEl); + + return input; + } + + /** + * Parse a specific output element. + * + * @param outputEl The output element. + * @param def The defintions element. + * @return A WSDL output element. + * @throws WSDLException + */ + protected Output parseOutput(Element outputEl, Definition def) throws WSDLException + { + Output output = def.createOutput(); + String name = DOMUtils.getAttribute(outputEl, Constants.ATTR_NAME); + QName messageName = null; + try + { + messageName = DOMUtils.getQualifiedAttributeValue(outputEl, Constants.ATTR_MESSAGE, Constants.ELEM_OUTPUT, false); + } + catch (Exception e) + { + //the call above fails if there is no qualified namespace for the message name + messageName = new QName(null, DOMUtils.getAttribute(outputEl, "message")); + } + + if (name != null) + { + output.setName(name); + } + + if (messageName != null) + { + Message message = def.getMessage(messageName); + + if (message == null) + { + message = def.createMessage(); + message.setQName(messageName); + def.addMessage(message); + } + + output.setMessage(message); + } + + Element tempEl = DOMUtils.getFirstChildElement(outputEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + output.setDocumentationElement(tempEl); + } + else + { + // XML Validation will catch this + DOMUtils.throwWSDLException(tempEl); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(output, outputEl); + + return output; + } + + /** + * Parse a specific fault element. + * + * @param faultEl The fault element to parse. + * @param def The definitions element. + * @return A WSDL fault element. + * @throws WSDLException + */ + protected Fault parseFault(Element faultEl, Definition def) throws WSDLException + { + Fault fault = def.createFault(); + String name = DOMUtils.getAttribute(faultEl, Constants.ATTR_NAME); + QName messageName = null; + try + { + messageName = DOMUtils.getQualifiedAttributeValue(faultEl, Constants.ATTR_MESSAGE, Constants.ELEM_INPUT, false); + } + catch (Exception e) + { + //the call above fails if there is no qualified namespace for the message name + messageName = new QName(null, DOMUtils.getAttribute(faultEl, "message")); + } + + if (name != null) + { + fault.setName(name); + } + + if (messageName != null) + { + Message message = def.getMessage(messageName); + + if (message == null) + { + message = def.createMessage(); + message.setQName(messageName); + def.addMessage(message); + } + + fault.setMessage(message); + } + + Element tempEl = DOMUtils.getFirstChildElement(faultEl); + + while (tempEl != null) + { + if (QNameUtils.matches(Constants.Q_ELEM_DOCUMENTATION, tempEl)) + { + fault.setDocumentationElement(tempEl); + } + else + { + // XML Validation will catch this + DOMUtils.throwWSDLException(tempEl); + } + + tempEl = DOMUtils.getNextSiblingElement(tempEl); + } + + // add the location of this element to elementLocations + setLocation(fault, faultEl); + + return fault; + } + + /** + * Set the messagegenerator for the reader. + * + * @param mg The message generator to set. + */ + public void setMessageGenerator(MessageGenerator mg) + { + messagegenerator = mg; + } + + /** + * Add the refObject to the elementLocation hashtable with the location defined in element. + * + * @param refObject The object to add. + * @param element The element that contains the location information. + */ + protected void setLocation(Object refObject, Element element) + { + try + { + ElementImpl elementImpl = (ElementImpl)element; + ElementLocation elementLocation = (ElementLocation)elementImpl.getUserData(); + if (elementLocation != null) + { + + elementLocations.put( + refObject, + new LocationHolder(elementLocation.getLineNumber(), elementLocation.getColumnNumber(), def.getDocumentBaseURI())); + } + } + catch (ClassCastException e) + { + } + } + /** + * Add a reader error to the list. + * + * @param parentobject The parent object of the object with the error. + * @param object The object with the error. + * @param error The error message. + */ + protected void addReaderError(Object parentobject, Object object, String error) + { + readerErrors.add(new ReaderError(parentobject, object, error)); + } + + /** + * Add a reader warning to the list. + * + * @param parentobject The parent object of the object with the error. + * @param object The object with the error. + * @param warning The warning message. + */ + protected void addReaderWarning(Object parentobject, Object object, String warning) + { + readerWarnings.add(new ReaderError(parentobject, object, warning)); + } + + /** + * Register all of the locations of the child elements of the extensibility + * element given. + * + * @param extElem The extensibility element whose child elements will be registered. + */ + + protected void registerChildElements(ExtensibilityElement extElem) + { + // only add those that are of type unknown. if they're known they + // will take care of themselves + if (extElem instanceof UnknownExtensibilityElement) + { + Element elem = ((UnknownExtensibilityElement)extElem).getElement(); + registerChildElementsRecursively(elem); + } + } + + /** + * Register the location of all of the child elements of elem. + * + * @param elem The element whose child elements will be registered. + */ + protected void registerChildElementsRecursively(Element elem) + { + if (elem instanceof ElementNSImpl) + { + setLocation(elem, elem); + + // call the method recursively for each child element + NodeList childNodes = elem.getChildNodes(); + + for (int i = 0; i < childNodes.getLength() || i < 5; i++) + { + Node n = childNodes.item(i); + // we only want nodes that are Elements + if (n instanceof Element) + { + Element child = (Element)n; + registerChildElementsRecursively(child); + } + } + } + } + /** + * Check that an element name matches the expected name. + * + * @param el The element with the name to check. + * @param qname The name to check against. + * @throws WSDLException + */ + private static void checkElementName(Element el, QName qname) throws WSDLException + { + if (!QNameUtils.matches(qname, el)) + { + WSDLException wsdlExc = new WSDLException(WSDLException.INVALID_WSDL, "Expected element '" + qname + "'."); + + wsdlExc.setLocation(XPathUtils.getXPathExprFromNode(el)); + + throw wsdlExc; + } + } + + /** + * Get the element locations hashtable. + * + * @return The element locations hashtable. + */ + public Hashtable getElementLocations() + { + return elementLocations; + } + + /** + * Get the reader errors. + * + * @return The reader errors. + */ + public List getReaderErrors() + { + return readerErrors; + } + + /** + * Get reader warnings. + * + * @return The reader warnings. + */ + public List getReaderWarnings() + { + return readerWarnings; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDLReaderImpl.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDLReaderImpl.java new file mode 100644 index 000000000..4e6ecd6a9 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/WSDLReaderImpl.java @@ -0,0 +1,427 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11; + +import java.io.Reader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.wsdl.Import; +import javax.wsdl.WSDLException; + +import org.apache.xerces.impl.XMLErrorReporter; +import org.apache.xerces.parsers.DOMParser; +import org.apache.xerces.parsers.StandardParserConfiguration; +import org.apache.xerces.xni.XNIException; +import org.eclipse.wst.wsdl.validation.internal.util.MessageGenerator; +import org.eclipse.wst.wsdl.validation.internal.xml.LineNumberDOMParser; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import com.ibm.wsdl.DefinitionImpl; +import com.ibm.wsdl.util.StringUtils; + +/** + * A WSDL reader that supports cyclic WSDL imports, schema imports and inline schemas. + * This reader is based on the WSDLReaderImpl from WSDL4J. + */ +public class WSDLReaderImpl +{ + protected MessageGenerator messagegenerator; + protected IWSDL11ValidationInfo wsdlvalinfo; + + /** + * Constructor. + * + * @param wsdlvalinfo The WSDL 1.1 validation info object to use. + */ + public WSDLReaderImpl(IWSDL11ValidationInfo wsdlvalinfo) + { + this.wsdlvalinfo = wsdlvalinfo; + } + + /** + * Parse the root document. This method will find all imports and parse them as + * well creating a WSDLDocument for each unique WSDL file. This method supports + * cyclic WSDL import statements such that file A can import file B and file B + * can import file A. + * + * @param documentBaseURI The base URI of the root document. + * @param defEl The definition element of the root document. + * @return An array of WSDLDocuments containing all of the unique files in the description. + * @throws WSDLException + */ + protected WSDLDocument[] parseDocument(String documentBaseURI, Element defEl) throws WSDLException + { + int initialImportArraySize = 20; + List[] filesAtDepth = new ArrayList[initialImportArraySize]; + Map filesImporting = new Hashtable(); + SortedSet parsedImports = new TreeSet(); + SortedSet importsToParse = new TreeSet(); + int maxdepth = 0; + + WSDLDocument rootdoc = new WSDLDocument(documentBaseURI, defEl, 0, messagegenerator, wsdlvalinfo); + String targetNamespace = rootdoc.getDefinition().getTargetNamespace(); + ImportHolder rootImport = new ImportHolder(targetNamespace, documentBaseURI, documentBaseURI, rootdoc, 0, null, messagegenerator, wsdlvalinfo); + rootImport.createWSDLImport(rootdoc); + parsedImports.add(rootImport); + List rootList = new ArrayList(); + filesImporting.put(rootImport.getLocation(), new ArrayList()); + rootList.add(rootdoc); + filesAtDepth[0] = rootList; + importsToParse.addAll(rootdoc.getImports()); + Set imps = rootdoc.getImports(); + Iterator impIter = imps.iterator(); + while(impIter.hasNext()) + { + ImportHolder imp = (ImportHolder)impIter.next(); + List tempList = new ArrayList(); + tempList.add(imp.getImportingDocument()); + filesImporting.put(imp.getLocation(), tempList); + } + + while(!importsToParse.isEmpty()) + { + ImportHolder imp = (ImportHolder)importsToParse.first(); + // It's important to initialize the import here so each import + // is only created once. In the case of reciprical imports this + // avoids an infinite loop. + imp.initialize(); + WSDLDocument impDoc = imp.getWSDLDocument(); + + importsToParse.remove(imp); + + parsedImports.add(imp); + + // Add new imports to the list of imports to parse. + // Remove all the imports that have already been parsed. + if(impDoc != null) + { + // Increate import array if necessary. + if(imp.getDepth() >= initialImportArraySize) + { + List[] tempArray = new List[filesAtDepth.length + initialImportArraySize]; + System.arraycopy(filesAtDepth, 0, tempArray, 0, filesAtDepth.length); + filesAtDepth = tempArray; + } + // Create the list for the depth if necessary. + int impDepth = imp.getDepth(); + if(filesAtDepth[impDepth] == null) + { + if(maxdepth < impDepth) + { + maxdepth = impDepth; + } + filesAtDepth[impDepth] = new ArrayList(); + } + filesAtDepth[imp.getDepth()].add(impDoc); + + Set imports = impDoc.getImports(); + ImportHolder[] importsArray = (ImportHolder[])imports.toArray(new ImportHolder[imports.size()]); + for(int i = 0; i < importsArray.length; i++) + { + ImportHolder ih = importsArray[i]; + // If already parsed, add the definition importing this file to the list. + if(filesImporting.containsKey(ih.getLocation())) + { + ((List)filesImporting.get(ih.getLocation())).add(ih.getImportingDocument()); + } + // Otherwise add it to the list to parse. + else + { + // Add this import to the list of files importing list. + List tempList = new ArrayList(); + tempList.add(ih.getImportingDocument()); + filesImporting.put(ih.getLocation(), tempList); + importsToParse.add(ih); + } + } + } + } + + // Add all of the imports to the respective documents. + Iterator importElementsIter = parsedImports.iterator(); + while(importElementsIter.hasNext()) + { + ImportHolder imp = (ImportHolder)importElementsIter.next(); + List files = (List)filesImporting.get(imp.getLocation()); + Iterator filesIter = files.iterator(); + while(filesIter.hasNext()) + { + WSDLDocument doc = (WSDLDocument)filesIter.next(); + + DefinitionImpl def = (DefinitionImpl)doc.getDefinition(); + Import impElem = imp.getImport(); + if(impElem != null) + { + def.addImport(impElem); + if(!imp.isWSDLFileImport()) + { + doc.addSchemas(imp.getSchemas()); + } + } + + } + } + + // Parse the WSDL documents. + // Parse the Messages. + for(int i = maxdepth; i >=0; i--) + { + List docs = filesAtDepth[i]; + Iterator docsIter = docs.iterator(); + while(docsIter.hasNext()) + { + WSDLDocument doc = (WSDLDocument)docsIter.next(); + doc.parseMessages(); + } + } + // Parse the Porttypes. + for(int i = maxdepth; i >=0; i--) + { + List docs = filesAtDepth[i]; + Iterator docsIter = docs.iterator(); + while(docsIter.hasNext()) + { + WSDLDocument doc = (WSDLDocument)docsIter.next(); + doc.parsePorttypes(); + } + } + // Parse the Bindings. + for(int i = maxdepth; i >=0; i--) + { + List docs = filesAtDepth[i]; + Iterator docsIter = docs.iterator(); + while(docsIter.hasNext()) + { + WSDLDocument doc = (WSDLDocument)docsIter.next(); + doc.parseBindings(); + } + } + // Parse the Services. + for(int i = maxdepth; i >=0; i--) + { + List docs = filesAtDepth[i]; + Iterator docsIter = docs.iterator(); + while(docsIter.hasNext()) + { + WSDLDocument doc = (WSDLDocument)docsIter.next(); + doc.parseServices(); + } + } + // Parse the Extensibility Elements. + for(int i = maxdepth; i >=0; i--) + { + List docs = filesAtDepth[i]; + Iterator docsIter = docs.iterator(); + while(docsIter.hasNext()) + { + WSDLDocument doc = (WSDLDocument)docsIter.next(); + doc.parseExtensibilityElements(); + } + } + + List wsdlDocs = new ArrayList(); + for(int i = maxdepth; i >=0; i--) + { + List docs = filesAtDepth[i]; + Iterator docsIter = docs.iterator(); + while(docsIter.hasNext()) + { + WSDLDocument doc = (WSDLDocument)docsIter.next(); + wsdlDocs.add(doc); + } + } + + return (WSDLDocument[])wsdlDocs.toArray(new WSDLDocument[wsdlDocs.size()]); + } + + /** + * Get the WSDL document. + * + * @param inputSource The source of the document being retrieved. + * @param desc The description of the document being retrieved. + * @return The WSDL document. + * @throws WSDLException + */ + public static Document getDocument(InputSource inputSource, String desc) throws WSDLException + { + try + { + StandardParserConfiguration configuration = new StandardParserConfiguration() + { + protected XMLErrorReporter createErrorReporter() + { + return new XMLErrorReporter() + { + public void reportError(String domain, String key, Object[] arguments, short severity) throws XNIException + { + boolean reportError = true; + if (key.equals("PrematureEOF")) + { + reportError = false; + } + + if (reportError) + { + super.reportError(domain, key, arguments, severity); + } + } + }; + } + }; + + ErrorHandler errorHandler = new ErrorHandler() + { + /* (non-Javadoc) + * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) + */ + public void error(SAXParseException exception) throws SAXException + { + // TODO Auto-generated method stub + + } + /* (non-Javadoc) + * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) + */ + public void fatalError(SAXParseException exception) throws SAXException + { + // TODO Auto-generated method stub + + } + /* (non-Javadoc) + * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) + */ + public void warning(SAXParseException exception) throws SAXException + { + // TODO Auto-generated method stub + + } + }; + + DOMParser builder = new LineNumberDOMParser(configuration); + builder.setErrorHandler(errorHandler); + builder.parse(inputSource); + Document doc = builder.getDocument(); + + return doc; + } + catch (Throwable t) + { + throw new WSDLException(WSDLException.PARSER_ERROR, "Problem parsing '" + desc + "'.", t); + } + } + + /** + * Read a WSDL document using a context URI and file URI. + * + * @param contextURI The context URI to use. + * @param wsdlURI The WSDL URI to use. + * @return An array of WSDLDocuments. + * @throws WSDLException + */ + public WSDLDocument[] readWSDL(String contextURI, String wsdlURI) throws WSDLException + { + try + { + URL contextURL = (contextURI != null) ? StringUtils.getURL(null, contextURI) : null; + URL url = StringUtils.getURL(contextURL, wsdlURI); + Reader reader = StringUtils.getContentAsReader(url); + InputSource inputSource = new InputSource(reader); + Document doc = getDocument(inputSource, wsdlURI); + reader.close(); + WSDLDocument[] wsdlDocs = null; + // only parse the document if it isn't empty + if(doc.getDocumentElement() != null) + { + wsdlDocs = readWSDL(url.toString(), doc); + } + return wsdlDocs; + } + catch (WSDLException e) + { + throw e; + } + catch (Throwable t) + { + throw new WSDLException( + WSDLException.OTHER_ERROR, + "Unable to resolve imported document at '" + wsdlURI + "'.", + t); + } + } + + /** + * Set the messagegenerator for the reader. + * + * @param mg The message generator to set. + */ + public void setMessageGenerator(MessageGenerator mg) + { + messagegenerator = mg; + } + + /** + * Read the WSDL document accessible via the specified + * URI into a WSDL definition. + * + * @param wsdlURI A URI pointing to a WSDL file. + * @return An array of WSDLDocuments. + */ + public WSDLDocument[] readWSDL(String wsdlURI) throws WSDLException + { + return readWSDL(null, wsdlURI); + } + + /** + * Read the WSDL document described by a URI and its definitions element. + * + * @param documentBaseURI The URI of the WSDL document. + * @param definitionsElement The definitions element for the WSDL document. + * @return An array of WSDLDocuments. + * @throws WSDLException + */ + protected WSDLDocument[] readWSDL(String documentBaseURI, + Element definitionsElement) + throws WSDLException + { + return parseDocument(documentBaseURI, definitionsElement); + } + + /** + * Read the specified WSDL document. + * + * @param documentBaseURI The document base URI. + * @param wsdlDocument The WSDL document. + * @return An array of WSDLDocuments. + */ + public WSDLDocument[] readWSDL(String documentBaseURI, Document wsdlDocument) + throws WSDLException + { + return readWSDL(documentBaseURI, wsdlDocument.getDocumentElement()); + } + + + +} + diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/http/HTTPValidator.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/http/HTTPValidator.java new file mode 100644 index 000000000..6780e48e2 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/http/HTTPValidator.java @@ -0,0 +1,334 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.http; + +import java.util.Iterator; +import java.util.List; +import java.util.ResourceBundle; + +import javax.wsdl.Binding; +import javax.wsdl.BindingInput; +import javax.wsdl.BindingOperation; +import javax.wsdl.Port; +import javax.wsdl.extensions.ExtensibilityElement; +import javax.wsdl.extensions.http.HTTPOperation; + +import org.eclipse.wst.wsdl.validation.internal.util.MessageGenerator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11Validator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo; + +import com.ibm.wsdl.BindingImpl; +import com.ibm.wsdl.BindingInputImpl; +import com.ibm.wsdl.BindingOperationImpl; +import com.ibm.wsdl.PortImpl; +import com.ibm.wsdl.extensions.http.HTTPAddressImpl; +import com.ibm.wsdl.extensions.http.HTTPBindingImpl; +import com.ibm.wsdl.extensions.http.HTTPOperationImpl; +import com.ibm.wsdl.extensions.http.HTTPUrlEncodedImpl; +import com.ibm.wsdl.extensions.http.HTTPUrlReplacementImpl; + +/** + * The HTTP validator is an extension WSDL validator that validates all elements in the HTTP + * namespace. + */ +public class HTTPValidator implements IWSDL11Validator +{ + private final String _ERROR_INVALID_PORT_ELEMENT = "_ERROR_INVALID_PORT_ELEMENT"; + private final String _ERROR_INVALID_BINDING_ELEMENT = "_ERROR_INVALID_BINDING_ELEMENT"; + private final String _ERROR_INVALID_BINDING_OPERATION_ELEMENT = "_ERROR_INVALID_BINDING_OPERATION_ELEMENT"; + private final String _ERROR_INVALID_BINDING_INPUT_ELEMENT = "_ERROR_INVALID_BINDING_INPUT_ELEMENT"; + private final String _ERROR_INVALID_HTTP_ELEMENT_FOR_LOCATION = "_ERROR_INVALID_HTTP_ELEMENT_FOR_LOCATION"; + private final String _ERROR_NO_LOCATION_FOR_ADDRESS = "_ERROR_NO_LOCATION_FOR_ADDRESS"; + private final String _ERROR_NO_HTTPBINDING_FOR_ADDRESS = "_ERROR_NO_HTTPBINDING_FOR_ADDRESS"; + private final String _ERROR_INVALID_BINDING_VERB = "_ERROR_INVALID_BINDING_VERB"; + private final String _ERROR_INVALID_LOCATION_URI = "_ERROR_INVALID_LOCATION_URI"; + private final String _ERROR_NO_HTTPBINDING_FOR_OPERATION = "_ERROR_NO_HTTPBINDING_FOR_OPERATION"; + private final String _ERROR_NOT_ONLY_ELEMENT_DEFINED = "_ERROR_NOT_ONLY_ELEMENT_DEFINED"; + private final String _ERROR_NO_HTTPOPERATION_FOR_URL = "_ERROR_NO_HTTPOPERATION_FOR_URL"; + + private final String GET = "GET"; + private final String POST = "POST"; + + private final String QUOTE = "'"; + private final String EMPTY_STRING = ""; + + private MessageGenerator messagegenerator; + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11Validator#validate(java.lang.Object, java.util.List, org.eclipse.wsdl.validate.wsdl11.IWSDL11ValidationInfo) + */ + public void validate(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + // Port HTTP definition + // make sure every port has only one address element defined + // if it is an address element, validate it + if (parents.get(0).getClass() == PortImpl.class) + { + if (element.getClass() == HTTPAddressImpl.class) + { + validateAddress(element, parents, valInfo); + } + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_PORT_ELEMENT, QUOTE + e.getElementType().getLocalPart() + QUOTE), + element); + } + } + + // Binding HTTP definition + // A HTTP Binding must have a verb of GET or POST + else if (parents.get(0).getClass() == BindingImpl.class) + { + if (element.getClass() == HTTPBindingImpl.class) + { + validateBinding(element, parents, valInfo); + } + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_BINDING_ELEMENT, QUOTE + e.getElementType().getLocalPart() + QUOTE), + element); + } + } + // Binding Operation HTTP definition + // A HTTP Operation has a location uri defined + else if (parents.get(0).getClass() == BindingOperationImpl.class) + { + if (element.getClass() == HTTPOperationImpl.class) + { + validateOperation(element, parents, valInfo); + } + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString( + _ERROR_INVALID_BINDING_OPERATION_ELEMENT, + QUOTE + e.getElementType().getLocalPart() + QUOTE), + element); + } + + } + else if (parents.get(0).getClass() == BindingInputImpl.class) + { + // validate the HTTP urlEncoded and urlReplacement + if (element.getClass() == HTTPUrlEncodedImpl.class || element.getClass() == HTTPUrlReplacementImpl.class) + { + validateHttpUrl(element, parents, valInfo); + } + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_BINDING_INPUT_ELEMENT, QUOTE + e.getElementType().getLocalPart() + QUOTE), + element); + } + } + + // in this case there has been a HTTP element defined that is not defined for this point in the HTTP namespace + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString( + _ERROR_INVALID_HTTP_ELEMENT_FOR_LOCATION, + QUOTE + e.getElementType().getLocalPart() + QUOTE), + element); + } + + } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.validator.IWSDL11Validator#setResourceBundle(java.util.ResourceBundle) + */ + public void setResourceBundle(ResourceBundle rb) + { + if (messagegenerator == null) + { + messagegenerator = new MessageGenerator(rb); + } + } + + /** + * Ensure that the HTTP address has a value specified for it's uri and that there is a HTTP Binding defined + * for the Binding specified in the port. + * + * @param element The HTTP address element. + * @param parents The list of parents of the HTTP address element. + * @param validatorcontroller The validator controller in charge of validation. + */ + protected void validateAddress(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + HTTPAddressImpl ha = (HTTPAddressImpl)element; + + String uri = ha.getLocationURI(); + if (uri == null || uri.equalsIgnoreCase(EMPTY_STRING)) + { + valInfo.addError(messagegenerator.getString(_ERROR_NO_LOCATION_FOR_ADDRESS), ha); + } + + Port port = (Port)parents.get(0); + + Binding binding = port.getBinding(); + + if (!hasHttpBinding(binding)) + { + valInfo.addError( + messagegenerator.getString( + _ERROR_NO_HTTPBINDING_FOR_ADDRESS, + QUOTE + binding.getQName().getLocalPart() + QUOTE, + QUOTE + port.getName() + QUOTE), + ha); + } + } + + /** + * Ensure the HTTP Binding defined is valid. A HTTP Binding must have a verb of GET or POST. + * + * @param element The HTTP binding element. + * @param parents The list of parents of the HTTP binding element. + * @param validatorcontroller The validator controller in charge of validation. + */ + protected void validateBinding(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + HTTPBindingImpl hb = (HTTPBindingImpl)element; + + String verb = hb.getVerb(); + + if (verb != null && !verb.equals(GET) && !verb.equals(POST)) + { + valInfo.addError(messagegenerator.getString(_ERROR_INVALID_BINDING_VERB, QUOTE + verb + QUOTE), element); + } + } + + /** + * An operation must have a location defined. A HTTP Binding must be specified to use an operation. + * + * @param element The HTTP operation element. + * @param parents The list of parents of the HTTP operation element. + * @param validatorcontroller The validator controller in charge of validation. + */ + protected void validateOperation(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + HTTPOperation ho = (HTTPOperation)element; + + String locationURI = ho.getLocationURI(); + + if (locationURI != null && locationURI.equalsIgnoreCase(EMPTY_STRING)) + { + valInfo.addError(messagegenerator.getString(_ERROR_INVALID_LOCATION_URI), element); + } + + Binding binding = (Binding)parents.get(1); + if (!hasHttpBinding(binding)) + { + valInfo.addError( + messagegenerator.getString(_ERROR_NO_HTTPBINDING_FOR_OPERATION, QUOTE + binding.getQName().getLocalPart() + QUOTE), + ho); + } + } + + /** + * Validate the HTTP urlReplacement or urlEncoded. Ensure the HTTP operation has been defined. + * Ensure that either element is the only element specified. + * + * @param element The HTTP binding operation element. + * @param parents The list of parents of the HTTP binding operation element. + * @param validatorcontroller The validator controller in charge of validation. + */ + protected void validateHttpUrl(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + BindingOperation operation = (BindingOperation)parents.get(1); + + String elementName; + if (element.getClass() == HTTPUrlEncodedImpl.class) + { + elementName = "urlEncoded"; + } + else if (element.getClass() == HTTPUrlReplacementImpl.class) + { + elementName = "urlReplacement"; + } + else + { + elementName = EMPTY_STRING; + } + + BindingInput input = (BindingInput)parents.get(0); + if (input.getExtensibilityElements().size() != 1) + { + valInfo.addError(messagegenerator.getString(_ERROR_NOT_ONLY_ELEMENT_DEFINED, elementName), element); + } + + if (!hasHttpOperation(operation)) + { + valInfo.addError( + messagegenerator.getString(_ERROR_NO_HTTPOPERATION_FOR_URL, QUOTE + operation.getName() + QUOTE, elementName), + element); + } + } + + /** + * Given a BindingOperation tells whether it has a HTTP operation defined for it. + * + * @param binding The HTTP binding operation element. + * @return True if there is an HTTP operation defined, false otherwise. + */ + protected boolean hasHttpOperation(BindingOperation operation) + { + if (operation != null) + { + List extelems = operation.getExtensibilityElements(); + if (extelems != null) + { + Iterator iextelems = extelems.iterator(); + while (iextelems.hasNext()) + { + if (iextelems.next().getClass() == HTTPOperationImpl.class) + { + return true; + } + } + } + } + return false; + } + + /** + * Given a binding returns true if it has a HTTP binding defined. + * + * @param binding The binding to check. + * @return True if there is an HTTP binding defined. + */ + protected boolean hasHttpBinding(Binding binding) + { + if (binding != null) + { + List extelems = binding.getExtensibilityElements(); + if (extelems != null) + { + Iterator iextelems = extelems.iterator(); + while (iextelems.hasNext()) + { + if (iextelems.next().getClass() == HTTPBindingImpl.class) + { + return true; + } + } + } + } + return false; + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/mime/MIMEValidator.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/mime/MIMEValidator.java new file mode 100644 index 000000000..54fcea8f2 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/mime/MIMEValidator.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.mime; + +import java.util.List; +import java.util.ResourceBundle; + +import org.eclipse.wst.wsdl.validation.internal.util.MessageGenerator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11Validator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo; + +/** + * The MIME validator plugs into the WSDL validator to provide + * validation for all elements in a WSDL document within the MIME namespace. + */ +public class MIMEValidator implements IWSDL11Validator +{ + protected MessageGenerator messagegenerator; + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11Validator#validate(java.lang.Object, java.util.List, org.eclipse.wsdl.validate.wsdl11.IWSDL11ValidationInfo) + */ + public void validate(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + + } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.validator.IWSDL11Validator#setResourceBundle(java.util.ResourceBundle) + */ + public void setResourceBundle(ResourceBundle rb) + { + if (messagegenerator == null) + { + messagegenerator = new MessageGenerator(rb); + } + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/soap/SOAPValidator.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/soap/SOAPValidator.java new file mode 100644 index 000000000..b3a95493e --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/soap/SOAPValidator.java @@ -0,0 +1,603 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.soap; + +import java.util.Iterator; +import java.util.List; +import java.util.ResourceBundle; + +import javax.wsdl.Binding; +import javax.wsdl.BindingFault; +import javax.wsdl.BindingOperation; +import javax.wsdl.Definition; +import javax.wsdl.Input; +import javax.wsdl.Message; +import javax.wsdl.Operation; +import javax.wsdl.Output; +import javax.wsdl.Part; +import javax.wsdl.Port; +import javax.wsdl.extensions.ExtensibilityElement; +import javax.wsdl.extensions.soap.SOAPOperation; +import javax.xml.namespace.QName; + +import org.eclipse.wst.wsdl.validation.internal.util.MessageGenerator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11Validator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo; + +import com.ibm.wsdl.BindingFaultImpl; +import com.ibm.wsdl.BindingImpl; +import com.ibm.wsdl.BindingInputImpl; +import com.ibm.wsdl.BindingOperationImpl; +import com.ibm.wsdl.BindingOutputImpl; +import com.ibm.wsdl.PortImpl; +import com.ibm.wsdl.extensions.soap.SOAPAddressImpl; +import com.ibm.wsdl.extensions.soap.SOAPBindingImpl; +import com.ibm.wsdl.extensions.soap.SOAPBodyImpl; +import com.ibm.wsdl.extensions.soap.SOAPFaultImpl; +import com.ibm.wsdl.extensions.soap.SOAPHeaderFaultImpl; +import com.ibm.wsdl.extensions.soap.SOAPHeaderImpl; +import com.ibm.wsdl.extensions.soap.SOAPOperationImpl; + +/** + * The SOAP validator plugs into the WSDL validator to provide + * validation for all elements in a WSDL document within the SOAP namespace. + * + */ +public class SOAPValidator implements IWSDL11Validator +{ + private final String _ERROR_INVALID_PORT_ELEMENT = "_ERROR_INVALID_PORT_ELEMENT"; + private final String _ERROR_INVALID_BINDING_ELEMENT = "_ERROR_INVALID_BINDING_ELEMENT"; + private final String _ERROR_INVALID_BINDING_OPERATION_ELEMENT = "_ERROR_INVALID_BINDING_OPERATION_ELEMENT"; + private final String _ERROR_INVALID_HEADER_BODY_ELEMENT = "_ERROR_INVALID_HEADER_BODY_ELEMENT"; + private final String _ERROR_INVALID_FAULT_ELEMENT = "_ERROR_INVALID_FAULT_ELEMENT"; + private final String _ERROR_INVALID_SOAP_ELEMENT_FOR_LOCATION = "_ERROR_INVALID_SOAP_ELEMENT_FOR_LOCATION"; + private final String _ERROR_NO_LOCATION_FOR_ADDRESS = "_ERROR_NO_LOCATION_FOR_ADDRESS"; + private final String _ERROR_NO_SOAPBINDING_FOR_ADDRESS = "_ERROR_NO_SOAPBINDING_FOR_ADDRESS"; + private final String _ERROR_INVALID_BINDING_STYLE = "_ERROR_INVALID_BINDING_STYLE"; + private final String _ERROR_INVALID_BINDING_URI = "_ERROR_INVALID_BINDING_URI"; + private final String _ERROR_INVALID_OPERATION_STYLE = "_ERROR_INVALID_OPERATION_STYLE"; + private final String _ERROR_NO_SOAPBINDING_FOR_OPERATION = "_ERROR_NO_SOAPBINDING_FOR_OPERATION"; + private final String _ERROR_INVALID_BODY_ENCODING_STYLE = "_ERROR_INVALID_BODY_ENCODING_STYLE"; + private final String _ERROR_INVALID_BODY_NAMESPACE_FOR_ENCODED = "_ERROR_INVALID_BODY_NAMESPACE_FOR_ENCODED"; + private final String _ERROR_INVALID_BODY_USE = "_ERROR_INVALID_BODY_USE"; + private final String _ERROR_INVALID_BODY_PART_NOT_TYPE = "_ERROR_INVALID_BODY_PART_NOT_TYPE"; + private final String _ERROR_INVALID_BODY_PART_UNDEFINED = "_ERROR_INVALID_BODY_PART_UNDEFINED"; + private final String _ERROR_NO_SOAPBINDING_FOR_BODY = "_ERROR_NO_SOAPBINDING_FOR_BODY"; + private final String _ERROR_HEADER_MESSAGE_UNDEFINED = "_ERROR_HEADER_MESSAGE_UNDEFINED"; + private final String _ERROR_HEADER_PART_UNDEFINED = "_ERROR_HEADER_PART_UNDEFINED"; + private final String _ERROR_HEADER_USE_UNDEFINED = "_ERROR_HEADER_USE_UNDEFINED"; + private final String _ERROR_HEADER_ENCODINGSTYLE_UNDEFINED = "_ERROR_HEADER_ENCODINGSTYLE_UNDEFINED"; + private final String _ERROR_HEADER_NAMESPACE_UNDEFINED = "_ERROR_HEADER_NAMESPACE_UNDEFINED"; + private final String _ERROR_NO_SOAPBINDING_FOR_HEADER = "_ERROR_NO_SOAPBINDING_FOR_HEADER"; + private final String _ERROR_HEADERFAULT_MESSAGE_UNDEFINED = "_ERROR_HEADERFAULT_MESSAGE_UNDEFINED"; + private final String _ERROR_HEADERFAULT_PART_UNDEFINED = "_ERROR_HEADERFAULT_PART_UNDEFINED"; + private final String _ERROR_HEADERFAULT_USE_UNDEFINED = "_ERROR_HEADERFAULT_USE_UNDEFINED"; + private final String _ERROR_HEADERFAULT_ENCODINGSTYLE_UNDEFINED = "_ERROR_HEADERFAULT_ENCODINGSTYLE_UNDEFINED"; + private final String _ERROR_HEADERFAULT_NAMESPACE_UNDEFINED = "_ERROR_HEADERFAULT_NAMESPACE_UNDEFINED"; + private final String _ERROR_INVALID_FAULT_NAME = "_ERROR_INVALID_FAULT_NAME"; + private final String _ERROR_INVALID_FAULT_ENCODING_STYLE = "_ERROR_INVALID_FAULT_ENCODING_STYLE"; + private final String _ERROR_INVALID_FAULT_NAMESPACE_FOR_ENCODED = "_ERROR_INVALID_FAULT_NAMESPACE_FOR_ENCODED"; + + private final String ENCODED = "encoded"; + private final String LITERAL = "literal"; + private final String RPC = "rpc"; + private final String DOCUMENT = "document"; + + private final String QUOTE = "'"; + private final String EMPTY_STRING = ""; + + protected MessageGenerator messagegenerator; + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11Validator#validate(java.lang.Object, java.util.List, org.eclipse.wsdl.validate.wsdl11.IWSDL11ValidationInfo) + */ + public void validate(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + // Port SOAP definition + // make sure every port has only one address element defined + // if it is an address element, validate it + if (parents.get(0).getClass() == PortImpl.class) + { + if (element.getClass() == SOAPAddressImpl.class) + { + validateAddress(element, parents, valInfo); + } + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_PORT_ELEMENT, QUOTE + e.getElementType().getLocalPart() + QUOTE), element); + } + } + + // Binding SOAP definition + // A SOAP Binding must have a style or rpc or document or no style defined - defaults to document + // Must have a transport uri defined - check if the uri is empty + else if (parents.get(0).getClass() == BindingImpl.class) + { + if (element.getClass() == SOAPBindingImpl.class) + { + validateBinding(element, parents, valInfo); + } + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_BINDING_ELEMENT, QUOTE + e.getElementType().getLocalPart() + QUOTE), element); + } + } + // Binding Operation SOAP definition + // A SOAP Operation may have a style defined in which case it must be document or rpc + // and may have a soapAction uri defined + else if (parents.get(0).getClass() == BindingOperationImpl.class) + { + if (element.getClass() == SOAPOperationImpl.class) + { + validateOperation(element, parents, valInfo); + } + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString( + _ERROR_INVALID_BINDING_OPERATION_ELEMENT, + QUOTE + e.getElementType().getLocalPart() + QUOTE), element); + } + + } + else if ( + parents.get(0).getClass() == BindingInputImpl.class || parents.get(0).getClass() == BindingOutputImpl.class) + { + // validate the SOAP body + if (element.getClass() == SOAPBodyImpl.class) + { + validateBody(element, parents, valInfo); + } + // valiate the SOAP header + else if (element.getClass() == SOAPHeaderImpl.class) + { + validateHeader(element, parents, valInfo); + } + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_HEADER_BODY_ELEMENT, QUOTE + e.getElementType().getLocalPart() + QUOTE), element); + } + } + else if (parents.get(0).getClass() == BindingFaultImpl.class) + { + if (element.getClass() == SOAPFaultImpl.class) + { + validateFault(element, parents, valInfo); + } + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_FAULT_ELEMENT, QUOTE + e.getElementType().getLocalPart() + QUOTE), element); + } + } + // in this case there has been a SOAP element defined that is not defined for this point in the SOAP namespace + else + { + ExtensibilityElement e = (ExtensibilityElement)element; + valInfo.addError( + messagegenerator.getString( + _ERROR_INVALID_SOAP_ELEMENT_FOR_LOCATION, + QUOTE + e.getElementType().getLocalPart() + QUOTE), element); + } + + } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.validator.IWSDL11Validator#setResourceBundle(java.util.ResourceBundle) + */ + public void setResourceBundle(ResourceBundle rb) + { + if (messagegenerator == null) + { + messagegenerator = new MessageGenerator(rb); + } + } + + /** + * Ensure that the SOAP address has a value specified for it's uri and that the binding has a SOAP + * Binding defined. + * + * @param element The SOAP address element. + * @param parents A list of parents of the SOAP address element. + * @param valInfo The validation info for this validation. + */ + protected void validateAddress(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + SOAPAddressImpl sa = (SOAPAddressImpl)element; + + String uri = sa.getLocationURI(); + if (uri == null || uri.equalsIgnoreCase(EMPTY_STRING)) + { + valInfo.addError(messagegenerator.getString(_ERROR_NO_LOCATION_FOR_ADDRESS), sa); + } + + Port port = (Port)parents.get(0); + + Binding binding = port.getBinding(); + + if (!hasSoapBinding(binding)) + { + valInfo.addError( + messagegenerator.getString( + _ERROR_NO_SOAPBINDING_FOR_ADDRESS, + QUOTE + binding.getQName().getLocalPart() + QUOTE, + QUOTE + port.getName() + QUOTE), sa); + } + } + + /** + * Ensure the SOAP Binding defined is valid. A SOAP Binding must have a style of rpc or document + * or no style defined (defaults to document.) A valid (non empty) URI must also be specified. + * + * @param element The SOAP binding element. + * @param parents A list of parents of the SOAP binding element. + * @param valInfo The validation info for this validation. + */ + protected void validateBinding(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + SOAPBindingImpl sb = (SOAPBindingImpl)element; + + String style = sb.getStyle(); + String uri = sb.getTransportURI(); + + if (style != null && !style.equalsIgnoreCase(RPC) && !style.equalsIgnoreCase(DOCUMENT)) + { + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_BINDING_STYLE, QUOTE + sb.getStyle() + QUOTE), element); + } + if (uri.equalsIgnoreCase(EMPTY_STRING)) + { + valInfo.addError(messagegenerator.getString(_ERROR_INVALID_BINDING_URI), element); + } + } + + /** + * An operation may have a style defined. If it is defined it must be rpc or document. It may also have a + * uri defined which must be non empty. It may have a soapAction defined as well. + * + * @param element The SOAP operation element. + * @param parents A list of parents of the SOAP operation element. + * @param valInfo The validation info for this validation. + */ + protected void validateOperation(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + SOAPOperation so = (SOAPOperation)element; + + String soapStyle = so.getStyle(); + + if (soapStyle != null && !soapStyle.equalsIgnoreCase(RPC) && !soapStyle.equalsIgnoreCase(DOCUMENT)) + { + valInfo.addError(messagegenerator.getString(_ERROR_INVALID_OPERATION_STYLE), element); + } + + Binding binding = (Binding)parents.get(1); + if (!hasSoapBinding(binding)) + { + valInfo.addError( + messagegenerator.getString(_ERROR_NO_SOAPBINDING_FOR_OPERATION, QUOTE + binding.getQName().getLocalPart() + QUOTE), so); + } + } + + /** + * Validate the SOAP body. If encoded a body must have an encodingStyle. Also, + * if specified, all of the parts listed must be defined parts and in the encoding use case, the parts + * must have types defined. + * + * @param element The SOAP body element. + * @param parents A list of parents of the SOAP body element. + * @param valInfo The validation info for this validation. + */ + protected void validateBody(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + SOAPBodyImpl sb = (SOAPBodyImpl)element; + + String use = sb.getUse(); + + // if the use = encoded then there must be encodingStyles. + if (use != null && use.equalsIgnoreCase(ENCODED)) + { + List encodingStyles = sb.getEncodingStyles(); + if (encodingStyles == null || encodingStyles.size() == 0) + { + valInfo.addError(messagegenerator.getString(_ERROR_INVALID_BODY_ENCODING_STYLE), sb); + } + } + else if (use != null && !use.equalsIgnoreCase(LITERAL)) + { + valInfo.addError(messagegenerator.getString(_ERROR_INVALID_BODY_USE, QUOTE + use + QUOTE), sb); + } + + //Check that the parts are valid + // parts must be defined in the message specified for the operation + List parts = sb.getParts(); + + if (parts != null) + { + Iterator partsIterator = parts.iterator(); + while (partsIterator.hasNext()) + { + String part = (String)partsIterator.next(); + BindingOperation bo = (BindingOperation)parents.get(1); + Operation o = bo.getOperation(); + + if (o != null && !o.isUndefined()) + { + // get the message from the input or output if it exists + Message mess = null; + if (parents.get(0).getClass() == BindingInputImpl.class) + { + Input input = o.getInput(); + + if (input != null) + { + mess = input.getMessage(); + } + } + else if (parents.get(0).getClass() == BindingOutputImpl.class) + { + Output output = o.getOutput(); + + if (output != null) + { + mess = output.getMessage(); + } + } + + if (mess != null && !mess.isUndefined()) + { + Part p = mess.getPart(part); + + if (p != null) + { + // if the use is encoded the parts must all have a type defined + if (use != null && use.equalsIgnoreCase(ENCODED)) + { + if (p.getTypeName() == null) + { + // part error - part needs to be type and isn't + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_BODY_PART_NOT_TYPE, QUOTE + part + QUOTE), sb); + } + } + } + else + { + //part error - part isn't defined + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_BODY_PART_UNDEFINED, QUOTE + part + QUOTE), sb); + } + } + else + { + //part error - input isn't defined + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_BODY_PART_UNDEFINED, QUOTE + part + QUOTE), sb); + } + } + else + { + // parts error - operation isn't defined + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_BODY_PART_UNDEFINED, QUOTE + part + QUOTE), sb); + } + } + } + + Binding binding = (Binding)parents.get(2); + if (!hasSoapBinding(binding)) + { + valInfo.addError( + messagegenerator.getString(_ERROR_NO_SOAPBINDING_FOR_BODY, QUOTE + binding.getQName().getLocalPart() + QUOTE), sb); + } + } + + /** + * A SOAP header must have a message, part and use defined. If the use is encoded, must + * also have a non-empty encodingStyle and namespace defined. + * A SOAP header may have headerfaults defined as well. + * + * @param element The SOAP header element. + * @param parents A list of parents of the SOAP header element. + * @param valInfo The validation info for this validation. + */ + protected void validateHeader(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + SOAPHeaderImpl soapHeader = (SOAPHeaderImpl)element; + + QName messageQName = soapHeader.getMessage(); + Message message = ((Definition)parents.get(parents.size() - 1)).getMessage(messageQName); + if (message == null) + { + // message undefined + valInfo.addError( + messagegenerator.getString(_ERROR_HEADER_MESSAGE_UNDEFINED, QUOTE + messageQName.getLocalPart() + QUOTE), soapHeader); + } + else + { + String partname = soapHeader.getPart(); + Part part = message.getPart(partname); + if (part == null) + { + // part undefined + valInfo.addError( + messagegenerator.getString( + _ERROR_HEADER_PART_UNDEFINED, + QUOTE + partname + QUOTE, + QUOTE + messageQName.getLocalPart() + QUOTE), soapHeader); + } + } + + String use = soapHeader.getUse(); + if (use != null && !use.equalsIgnoreCase(LITERAL) && !use.equalsIgnoreCase(ENCODED)) + { + // use undefined + valInfo.addError( + messagegenerator.getString(_ERROR_HEADER_USE_UNDEFINED, QUOTE + use + QUOTE), soapHeader); + } + + if (use.equalsIgnoreCase(ENCODED)) + { + List encodingStyles = soapHeader.getEncodingStyles(); + if (encodingStyles == null || encodingStyles.isEmpty()) + { + // no encodingStyle defined + valInfo.addError(messagegenerator.getString(_ERROR_HEADER_ENCODINGSTYLE_UNDEFINED), soapHeader); + } + + String namespace = soapHeader.getNamespaceURI(); + if (namespace == null || namespace.equalsIgnoreCase(EMPTY_STRING)) + { + // no namespace defined + valInfo.addError(messagegenerator.getString(_ERROR_HEADER_NAMESPACE_UNDEFINED), soapHeader); + } + } + + List headerFaults = soapHeader.getSOAPHeaderFaults(); + if (headerFaults != null) + { + Iterator iheaderFaults = headerFaults.iterator(); + while (iheaderFaults.hasNext()) + { + validateHeaderFault(iheaderFaults.next(), parents, valInfo); + } + } + + Binding binding = (Binding)parents.get(2); + if (!hasSoapBinding(binding)) + { + valInfo.addError( + messagegenerator.getString(_ERROR_NO_SOAPBINDING_FOR_HEADER, QUOTE + binding.getQName().getLocalPart() + QUOTE), soapHeader); + } + } + + /** + * A SOAP headerfault must have a message, part and use defined. If the use is encoded, must + * also have a non-empty encodingStyle and namespace defined. + * + * @param element The SOAP header fault element. + * @param parents A list of parents of the SOAP header fault element. + * @param valInfo The validation info for this validation. + */ + protected void validateHeaderFault(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + SOAPHeaderFaultImpl soapHeaderFault = (SOAPHeaderFaultImpl)element; + + QName messageQName = soapHeaderFault.getMessage(); + Message message = ((Definition)parents.get(parents.size() - 1)).getMessage(messageQName); + if (message == null) + { + // message undefined + valInfo.addError( + messagegenerator.getString(_ERROR_HEADERFAULT_MESSAGE_UNDEFINED, QUOTE + messageQName.getLocalPart() + QUOTE), soapHeaderFault); + } + else + { + String partname = soapHeaderFault.getPart(); + Part part = message.getPart(partname); + if (part == null) + { + // part undefined + valInfo.addError( + messagegenerator.getString( + _ERROR_HEADERFAULT_PART_UNDEFINED, + QUOTE + partname + QUOTE, + QUOTE + messageQName.getLocalPart() + QUOTE), soapHeaderFault); + } + } + + String use = soapHeaderFault.getUse(); + if (use != null && !use.equalsIgnoreCase(LITERAL) && !use.equalsIgnoreCase(ENCODED)) + { + // use undefined + valInfo.addError( + messagegenerator.getString(_ERROR_HEADERFAULT_USE_UNDEFINED, QUOTE + use + QUOTE), soapHeaderFault); + } + + if (use.equalsIgnoreCase(ENCODED)) + { + List encodingStyles = soapHeaderFault.getEncodingStyles(); + if (encodingStyles == null || encodingStyles.isEmpty()) + { + // no encodingStyle defined + valInfo.addError( + messagegenerator.getString(_ERROR_HEADERFAULT_ENCODINGSTYLE_UNDEFINED), soapHeaderFault); + } + + String namespace = soapHeaderFault.getNamespaceURI(); + if (namespace == null || namespace.equalsIgnoreCase(EMPTY_STRING)) + { + // no namespace defined + valInfo.addError( + messagegenerator.getString(_ERROR_HEADERFAULT_NAMESPACE_UNDEFINED), soapHeaderFault); + } + } + } + + /** + * Validate the SOAP fault. A SOAP fault must have a name defined that corresponds with the name + * specified in the portType. If encoded a fault must have an encodingStyle and a namespaceURI. + * + * @param element The SOAP fault element. + * @param parents A list of parents of the SOAP fault element. + * @param validationInfo The validation info for this validation. + */ + protected void validateFault(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + SOAPFaultImpl fault = (SOAPFaultImpl)element; + + String name = fault.getName(); + + String parentName = ((BindingFault)parents.get(0)).getName(); + + if (!name.equals(parentName)) + { + valInfo.addError( + messagegenerator.getString(_ERROR_INVALID_FAULT_NAME, QUOTE + name + QUOTE, QUOTE + parentName + QUOTE), fault); + } + + } + + /** + * Method hasSoapBinding. - helper Method + * Given a binding returns true if it has a SOAP binding defined. + * + * @param binding - the SOAP binding to check + * @return true if a binding has a SOAP binding defined, false otherwise + */ + protected boolean hasSoapBinding(Binding binding) + { + if (binding != null) + { + List extelems = binding.getExtensibilityElements(); + if (extelems != null) + { + Iterator iextelems = extelems.iterator(); + while (iextelems.hasNext()) + { + if (iextelems.next().getClass() == SOAPBindingImpl.class) + { + return true; + } + } + } + } + return false; + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/DOMError.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/DOMError.java new file mode 100644 index 000000000..329b1118b --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/DOMError.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2005 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd; + +/** + * This interface is used to signify error levels that are the result of + * XMLParseException. They conceptually correspond to org.w3c.dom.DOMError but + * we use our own interface, since DOMError is not in Java 1.4, and its + * package changes from Xerces 2.6.2 and Xerces 2.7.0. + */ + +public interface DOMError { + int SEVERITY_WARNING = 1; + int SEVERITY_ERROR = 2; + int SEVERITY_FATAL_ERROR = 3; +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/FileEntityResolver.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/FileEntityResolver.java new file mode 100644 index 000000000..2119778f3 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/FileEntityResolver.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.xerces.xni.XMLResourceIdentifier; +import org.apache.xerces.xni.XNIException; +import org.apache.xerces.xni.parser.XMLEntityResolver; +import org.apache.xerces.xni.parser.XMLInputSource; +import org.eclipse.wst.wsdl.validation.internal.util.LazyURLInputStream; + +/** + * Entity resolve to resolve file entities. + */ +public class FileEntityResolver implements XMLEntityResolver +{ + + /** + * @see org.apache.xerces.xni.parser.XMLEntityResolver#resolveEntity(org.apache.xerces.xni.XMLResourceIdentifier) + */ + public XMLInputSource resolveEntity(XMLResourceIdentifier resource) throws XNIException, IOException + { + String publicId = resource.getPublicId(); + String systemId = resource.getExpandedSystemId(); + String namespace = resource.getNamespace(); + String url = null; + if(systemId != null) + { + url = systemId; + } + else if(publicId != null) + { + url = publicId; + } + else if(namespace != null) + { + url = namespace; + } + if(url != null) + { + InputStream is = new LazyURLInputStream(url); + return new XMLInputSource(publicId, resource.getExpandedSystemId(), resource.getExpandedSystemId(), is, null); + } + return null; + } + +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/InlineSchemaGenerator.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/InlineSchemaGenerator.java new file mode 100644 index 000000000..1792ec172 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/InlineSchemaGenerator.java @@ -0,0 +1,667 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; + +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import com.ibm.wsdl.Constants; + +/** + * Generate a String representation of a schema for an inline schema. Will add imports for unresolved + * namespaces. + */ +public class InlineSchemaGenerator +{ + protected static final String SOAP_ENCODING_URI = "http://schemas.xmlsoap.org/soap/encoding/"; + protected static final String FILE_PREFIX = "file:///"; + protected static final String XMLNS = "xmlns"; + protected static final String TARGETNAMESPACE = "targetNamespace"; + protected static final String NAMESPACE = "namespace"; + protected static final String IMPORT = "import"; + protected static final String INCLUDE = "include"; + protected static final String SCHEMA = "schema"; + protected static final String SCHEMALOCATION = "schemaLocation"; + protected static final String TYPE = "type"; + protected static final String NAME = "name"; + protected static final String[] ignoreNamespaces = + { Constants.NS_URI_XSD_1999, Constants.NS_URI_XSD_2000, Constants.NS_URI_XSD_2001 }; + + protected static InlineSchemaGenerator instance = null; + + /** + * Constructor. + */ + protected InlineSchemaGenerator() + { + } + + /** + * Get the instance of the InlineSchemaGenerator. + * + * @return The instance of the inline schema generator. + */ + protected static InlineSchemaGenerator getInstance() + { + if (instance == null) + { + instance = new InlineSchemaGenerator(); + } + return instance; + } + + /** + * Create a string representation of a schema from the element provided. + * + * @param element The root element of the schema. + * @param elements A list of the elements in the schema in order. + * @param filelocation The URI of the file that contains the schema. + * @param validImportNSs A set of namespaces for which it's valid to create import statements. + * @return A string representation of a schema. + */ + public static String createXSDString(Element element, List elements, String filelocation, Set validImportNSs) + { + return InlineSchemaGenerator.createXSDString(element, elements, filelocation, new Hashtable(), validImportNSs); + } + + /** + * Creates a String representing the schema model with the root element of + * extElem. Calls createXSDStringRecursively to take care of building the String + * after it obtains the Element from the UnknownExtensibilityElement. + * + * @param element The root element of the schema. + * @param elements A list to contain the elements in the schema in order. + * @param filelocation The location of the file the schema is located in. + * @param parentNSs A hashtable of parent namespaces to used to resolve prefixes. + * @param validImportNSs A set of namespaces for which it's valid to create import statements. + * @return A string representation of the schema with the root element 'element'. + */ + public static String createXSDString(Element element, List elements, String filelocation, Hashtable parentNSs, Set validImportNSs) + { + Set importNSs = new TreeSet(); + importNSs.addAll(validImportNSs); + importNSs.add(SOAP_ENCODING_URI); + + InlineSchemaGenerator schemaGenerator = InlineSchemaGenerator.getInstance(); + Hashtable nsResolver = schemaGenerator.getNSResolver(element); + List reqns = schemaGenerator.getNamespacePrefixes(element); + Hashtable reqNSDecl = schemaGenerator.resolveNamespaces(reqns, nsResolver, parentNSs); + //Hashtable reqNSDecl = schemaGenerator.getRequiredNSDeclarations(reqns, nsResolver, parentNSs); + List importNS = schemaGenerator.getImportNamespaces(element); + reqns = schemaGenerator.removeImports(reqns, importNS); + reqns = schemaGenerator.removeLocalNamespaces(reqns, element); + reqns = schemaGenerator.restrictImports(reqns, validImportNSs); + return schemaGenerator.createXSDStringRecursively(element, elements, reqns, reqNSDecl, filelocation); + } + /** + * Returns true if the SOAP encoding namespace is required but not imported. + * + * @param element The root element of the schema. + * @param filelocation The location of the file containing the schema. + * @param parentNSs A hashtable of the parent namespaces. + * @return True if the soap encoding namespace is required but not imported, false otherwise. + */ + public static boolean soapEncodingRequiredNotImported(Element element, String filelocation, Hashtable parentNSs) + { + InlineSchemaGenerator schemaGenerator = InlineSchemaGenerator.getInstance(); + Hashtable nsResolver = schemaGenerator.getNSResolver(element); + List reqns = null; + + reqns = schemaGenerator.getNamespacePrefixes(element); + schemaGenerator.resolveNamespaces(reqns, nsResolver, parentNSs); + //schemaGenerator.resolveUndeclaredNamespaces(reqns, parentNSs); + List importNS = schemaGenerator.getImportNamespaces(element); + reqns = schemaGenerator.removeImports(reqns, importNS); + reqns = schemaGenerator.removeLocalNamespaces(reqns, element); + return schemaGenerator.checkSOAPEncodingRequired(reqns); + } + /** + * Resolve the undeclared namespaces. + * + * @param unresolvedNSs A list of unresolved namespaces. + * @param nsResolver The namespace resolver to use. + * @return A hashtable of prefixes and namespaces. + */ +// protected Hashtable resolveUndeclaredNamespaces(List unresolvedNSs, Hashtable nsResolver) +// { +// Hashtable namespaces = new Hashtable(); +// if (unresolvedNSs != null && !unresolvedNSs.isEmpty() && nsResolver != null) +// { +// for (int i = unresolvedNSs.size() - 1; i >= 0; i--) +// { +// String ns = (String)unresolvedNSs.get(i); +// if (ns.equals("")) +// { +// ns = XMLNS; +// } +// else +// { +// ns = XMLNS + ":" + ns; +// } +// if (nsResolver.containsKey(ns)) +// { +// //namespaces.remove(i); +// //namespaces.add(i, nsResolver.get(ns)); +// namespaces.put(ns, nsResolver.get(ns)); +// unresolvedNSs.remove(i); +// unresolvedNSs.add(i, nsResolver.get(ns)); +// } +// } +// } +// return namespaces; +// } + + /** + * This recursive method creates the schema String from the root Element. + * + * @param elem The root element of the schema. + * @param elements A list to be created of the elements in the schema in order. + * @param requiredNamespaces A list of required namespaces. + * @param reqNSDecl A hashtable of required namespace declarations. + * @param filelocation The uri of the file that contains this schema. + * @return A string representation of this schema. + */ + protected String createXSDStringRecursively( + Element elem, + List elements, + List requiredNamespaces, + Hashtable reqNSDecl, + String filelocation) + { + if (elem == null) + return ""; // just in case + + elements.add(elem); + + StringBuffer xsdString = new StringBuffer(); + String elementName = elem.getTagName(); + xsdString.append("<").append(elementName); + + boolean foundSchemaLocation = false; // flag if schemalocation is defined + String namespace = ""; // the namespace if we're checking an import or include + String namePrefix = ""; // the xmlns prefix used for the elements + // Get all of the attributes for this element and append them to the xsdString + NamedNodeMap atts = elem.getAttributes(); + for (int i = 0; i < atts.getLength(); i++) + { + Node n = atts.item(i); + xsdString.append(" ").append(n.getNodeName()).append("=\""); + String nodeName = n.getNodeName(); + if (nodeName.equalsIgnoreCase(SCHEMALOCATION) && filelocation != null) + { + foundSchemaLocation = true; + String relativePath = n.getNodeValue(); + xsdString.append(relativePath).append("\""); + } + else + { + String nodeValue = n.getNodeValue(); + if (nodeName.equalsIgnoreCase(NAMESPACE)) + { + namespace = nodeValue; + } + // get the name prefix for this schema to use in generating import statements + else if (nodeName.indexOf(XMLNS) != -1) + { + + if (nodeValue.equalsIgnoreCase(elem.getNamespaceURI())) + { + namePrefix = nodeName; + if (namePrefix.equalsIgnoreCase(XMLNS)) + { + namePrefix = ""; + } + else + { + namePrefix = namePrefix.substring(6) + ":"; + } + } + } + // Replace old schema namespaces with the new schema namespace. + if(nodeValue.equals(Constants.NS_URI_XSD_1999) || nodeValue.equals(Constants.NS_URI_XSD_2000)) + { + nodeValue = Constants.NS_URI_XSD_2001; + } + xsdString.append(nodeValue).append("\""); + } + } + if (elementName.equalsIgnoreCase("import") && !foundSchemaLocation) + { + xsdString.append(" ").append(SCHEMALOCATION).append("=\"").append(namespace).append("\""); + } + // add in any required NS declarations from parent elements + if (reqNSDecl != null) + { + Enumeration keys = reqNSDecl.keys(); + while (keys.hasMoreElements()) + { + String key = (String)keys.nextElement(); + String declNS = (String)reqNSDecl.get(key); + if(declNS.equals(Constants.NS_URI_XSD_1999) || declNS.equals(Constants.NS_URI_XSD_2000)) + { + declNS = Constants.NS_URI_XSD_2001; + } + xsdString.append(" ").append(key).append("=\"").append(declNS).append("\""); + } + + } + xsdString.append(">"); + if (requiredNamespaces != null) + { + Iterator iRequiredNamespaces = requiredNamespaces.iterator(); + while (iRequiredNamespaces.hasNext()) + { + String ns = (String)iRequiredNamespaces.next(); + + xsdString + .append("<") + .append(namePrefix) + .append(IMPORT) + .append(" ") + .append(NAMESPACE) + .append("=\"") + .append(ns) + .append("\" ") + .append(SCHEMALOCATION) + .append("=\"") + .append(ns) + .append("\"/>"); + } + + } + xsdString.append("\n"); + + // call the method recursively for each child element + NodeList childNodes = elem.getChildNodes(); + + for (int i = 0; i < childNodes.getLength() || i < 5; i++) + { + Node n = childNodes.item(i); + // we only want nodes that are Elements + if (n instanceof Element) + { + Element child = (Element)n; + xsdString.append(createXSDStringRecursively(child, elements, null, null, filelocation)); + } + } + + xsdString.append("</").append(elem.getTagName()).append(">"); + + return xsdString.toString(); + + } + /** + * Get a list of all the namespace prefixes that are used for elements or types in the schema. + * + * @param elem The root element of the schema to check for namespace prefixes. + * @return A list of namespace prefixes for the element and all its children. + */ + protected List getNamespacePrefixes(Element elem) + { + List namespace = new ArrayList(); + + // call the method recursively for each child element + // register all the child types first + NodeList childNodes = elem.getChildNodes(); + int numChildren = childNodes.getLength(); + for (int i = 0; i < numChildren; i++) + { + Node n = childNodes.item(i); + // we only want nodes that are Elements + if (n instanceof Element) + { + Element child = (Element)n; + List childns = getNamespacePrefixes(child); + for (int j = childns.size() - 1; j >= 0; j--) + { + String ns = (String)childns.get(j); + + if (!namespace.contains(ns)) + { + namespace.add(ns); + } + } + } + } + // Add the namespace of the current element + String elemNS = elem.getPrefix(); + // if there is no namespace prefix set it to the empty prefix. + if(elemNS == null) + { + elemNS = ""; + } + if (!namespace.contains(elemNS)) + { + namespace.add(elemNS); + } + // now add all of the current element's namespaces + // don't include import and schema elements + String localname = elem.getLocalName(); + if (!localname.equals(IMPORT) && !localname.equals(INCLUDE) && !localname.equals(SCHEMA)) + { + NamedNodeMap atts = elem.getAttributes(); + for (int i = 0; i < atts.getLength(); i++) + { + Node n = atts.item(i); + + String nodeName = n.getNodeName(); + // removed restriction that we're only looking at types + // if (nodeName.equalsIgnoreCase(TYPE)) + // { + // don't take namespace info from attributes defining namespaces. + // that includes xmlns, targetNamespace + if (nodeName.indexOf(XMLNS) != -1 || nodeName.equals(TARGETNAMESPACE) || nodeName.equals(NAME)) + { + continue; + } + // Grab namespace prefixes from attributes. + else + { + int colonIndex = nodeName.indexOf(":"); + if(colonIndex != -1 && (colonIndex + 1 < nodeName.length() && nodeName.charAt(colonIndex + 1) != '/')) + { + String prefix = nodeName.substring(0, colonIndex); + if (!namespace.contains(prefix)) + { + + namespace.add(prefix); + } + } + } + String nodeValue = n.getNodeValue(); + + + int colonIndex = nodeValue.indexOf(":"); + // Don't take namespace info from attributes with the default namespace, that is attributes + // that are not prefixed. (colonIndex == -1) + // If the colonIndex is followed by a / then it is a URI and not + // namespace qualified. + if (colonIndex == -1 || (colonIndex + 1 < nodeValue.length() && nodeValue.charAt(colonIndex + 1) == '/')) + { + continue; + } + // here we have found a colon delimiter so we need the namespace defined here + else + { + nodeValue = nodeValue.substring(0, colonIndex); + } + if (!namespace.contains(nodeValue)) + { + + namespace.add(nodeValue); + } + } + } + + return namespace; + + } + + /** + * Get a list of all the namespaces that have an import statement. + * + * @param elem The root element of the schema. + * @return A list of all the namespaces that are imported. + */ + protected List getImportNamespaces(Element elem) + { + List namespace = new Vector(); + + // call the method recursively for each child element + // register all the child types first + NodeList childNodes = elem.getChildNodes(); + + for (int i = 0; i < childNodes.getLength() || i < 5; i++) + { + Node n = childNodes.item(i); + // we only want nodes that are Elements + if (n instanceof Element) + { + Element child = (Element)n; + List childns = getImportNamespaces(child); + for (int j = childns.size() - 1; j >= 0; j--) + { + String ns = (String)childns.get(j); + if (!namespace.contains(ns)) + { + namespace.add(ns); + } + } + } + } + // if this is an import element get the namespace and add it to the list + if (elem.getLocalName().equalsIgnoreCase(IMPORT)) + { + NamedNodeMap atts = elem.getAttributes(); + for (int i = 0; i < atts.getLength(); i++) + { + Node n = atts.item(i); + + String nodeName = n.getNodeName(); + if (nodeName.equalsIgnoreCase(NAMESPACE)) + { + String nodeValue = n.getNodeValue(); + if (!namespace.contains(nodeValue)) + { + + namespace.add(nodeValue); + } + } + } + } + + return namespace; + + } + + /** + * Return a Hashtable with namespace prefixes as keys from the given element. + * + * @param elem The root element of the schema. + * @return A hashtable with namespace prefixes mapped to namespaces. + */ + protected Hashtable getNSResolver(Element elem) + { + Hashtable nsResolver = new Hashtable(); + + NamedNodeMap atts = elem.getAttributes(); + for (int i = 0; i < atts.getLength(); i++) + { + Node n = atts.item(i); + + String nodeName = n.getNodeName(); + if (nodeName.indexOf(XMLNS) != -1) + { + String nodeValue = n.getNodeValue(); + String namePrefix = nodeName; + + if (namePrefix.equalsIgnoreCase(XMLNS)) + { + namePrefix = ""; + } + else + { + namePrefix = namePrefix.substring(6); + } + nsResolver.put(namePrefix, nodeValue); + + } + } + return nsResolver; + + } + + /** + * Resolve the namespaces in the given namespaces list with the two namespace + * resolver hashtables provided. Return a list of all the namespace that need + * to be declared. + * First resolve against the local namespaces with nsResolver. + * Next resolve against the parent namespace with parentNSResolver. + * A side affect of this method is the namespaces list is left with only those + * namespaces that are resolved and the resolved entities are placed in the + * list instead of the original entries. + * For ex. If you provide a list such as {xsd, intf} and only xsd can be resolved + * you will end up with the list {http://www.w3.org/2001/XMLSchema} + * + * @param namespaces The list of namespaces to resolve. + * @param nsResolver The hashtable to be used as the local resolver. + * @param parentNSResolver The hashtable to be used as the parent namespace resolver. + * @return A Hashtable of namespaces that must be declared. + */ + protected Hashtable resolveNamespaces(List namespaces, Hashtable nsResolver, Hashtable parentNSResolver) + { + Hashtable reqNSDecl = new Hashtable(); + if (namespaces != null && !namespaces.isEmpty() && nsResolver != null) + { + for (int i = namespaces.size() - 1; i >= 0; i--) + { + String ns = (String)namespaces.get(i); + // Remove the namespace from the list. + namespaces.remove(i); + // First try to resolve against the local namespace resolver. + if (nsResolver.containsKey(ns)) + { + Object resolvedNS = nsResolver.get(ns); + // Only add the namespace if it's not already in the list. + if(!namespaces.contains(resolvedNS)) + { + namespaces.add(i, nsResolver.get(ns)); + } + } + // Next try to resolve against the parent namespace resolver. + else + { + if (ns.equals("")) + { + ns = XMLNS; + } + else + { + ns = XMLNS + ":" + ns; + } + if (parentNSResolver.containsKey(ns)) + { + Object resolvedNS = parentNSResolver.get(ns); + // Only add the namespace if it's not already in the list. + if(!namespaces.contains(resolvedNS)) + { + namespaces.add(i, resolvedNS); + } + // Still need to declare the namespace though. + reqNSDecl.put(ns, resolvedNS); + } + } + + } + } + return reqNSDecl; + } + + /** + * Remove any namespace from the namespaces list if it is in the import list. + * + * @param namespaces The namespaces list. + * @param importedNamespaces A list of imported namespaces. + * @return The list of namespaces without the imported namespaces. + */ + protected List removeImports(List namespaces, List importedNamespaces) + { + if (namespaces != null && importedNamespaces != null && !importedNamespaces.isEmpty()) + { + Iterator iImportedNS = importedNamespaces.iterator(); + while (iImportedNS.hasNext()) + { + String iNS = (String)iImportedNS.next(); + + namespaces.remove(iNS); + } + } + return namespaces; + } + + /** + * Remove the local namespace for the schema and the namespaces listed in the ignoreNamespaces + * list from the namespaces list provided. + * + * @param namespaces The list of local namespaces. + * @param elem The root element of the schema. + * @return The list of namespaces with the local namespaces removed. + */ + protected List removeLocalNamespaces(List namespaces, Element elem) + { + if (namespaces != null && elem != null) + { + String ns = elem.getAttribute(TARGETNAMESPACE); + namespaces.remove(ns); + + for (int i = ignoreNamespaces.length - 1; i >= 0; i--) + { + // keep removing the namespace until it is not in the list + if (namespaces.remove(ignoreNamespaces[i])) + { + i++; + } + } + } + return namespaces; + } + + /** + * Remove all the namespaces in the namespaces list that aren't contained in the + * validImportNSs set. + * + * @param namespaces A list of namespaces. + * @param validImportNSs A set of valid namespaces. + * @return A list of namespaces that does not contain any members which aren't in the validImportNSs set. + */ + protected List restrictImports(List namespaces, Set validImportNSs) + { + Iterator nsIter = namespaces.iterator(); + while(nsIter.hasNext()) + { + String ns = (String)nsIter.next(); + if(!validImportNSs.contains(ns)) + { + namespaces.remove(ns); + nsIter = namespaces.iterator(); + } + } + return namespaces; + } + + /** + * Returns true if the SOAP encoding namespace is in the list of required namespaces, + * false otherwise. + * + * @param reqns The list of namespaces to check for the SOAP encoding namespace. + * @return True if the SOAP encoding namespaces is in the list, false otherwise. + */ + protected boolean checkSOAPEncodingRequired(List reqns) + { + if (reqns.contains(SOAP_ENCODING_URI)) + { + return true; + } + return false; + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/InlineSchemaValidator.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/InlineSchemaValidator.java new file mode 100644 index 000000000..5b58f211c --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/InlineSchemaValidator.java @@ -0,0 +1,300 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.TreeSet; + +import javax.wsdl.Definition; +import javax.wsdl.Types; +import javax.wsdl.extensions.UnknownExtensibilityElement; + +import org.apache.xerces.xni.parser.XMLEntityResolver; +import org.apache.xerces.xs.XSModel; +import org.eclipse.wst.wsdl.validation.internal.util.ErrorMessage; +import org.eclipse.wst.wsdl.validation.internal.util.MessageGenerator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11Validator; +import org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11ValidationInfo; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import com.ibm.wsdl.Constants; + +/** + * Plugin validator for the WSDL Validation framework. Validates inline schema found in a WSDL document. + */ +public class InlineSchemaValidator implements IWSDL11Validator +{ + private final String _WARN_OLD_SCHEMA_NAMESPACE = "_WARN_OLD_SCHEMA_NAMESPACE"; + private final String _WARN_SOAPENC_IMPORTED_SCHEMA = "_WARN_SOAPENC_IMPORTED_SCHEMA"; + private final String EMPTY_STRING = ""; + private final String QUOTE = "'"; + MessageGenerator messagegenerator = null; + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.IWSDL11Validator#validate(java.lang.Object, java.util.List, org.eclipse.wsdl.validate.wsdl11.WSDL11ValidationInfo) + */ + public void validate(Object element, List parents, IWSDL11ValidationInfo valInfo) + { + List elements = new ArrayList(); + UnknownExtensibilityElement elem = (UnknownExtensibilityElement) element; + Definition wsdlDefinition = (Definition) parents.get(parents.size() - 1); + String baseLocation = wsdlDefinition.getDocumentBaseURI(); + // Add in the namespaces defined in the doc already that aren't defined locally in this schema. + // There is no need to check for namespaces other then in the defintions and types elements as + // inline schema can not have any other parents and must have there two parents. + // First take care of the definitions element + + // create the inline schema string + Element w3celement = elem.getElement(); + Hashtable parentnamespaces = getNamespaceDeclarationsFromParents(wsdlDefinition,w3celement); + String targetNamespace = w3celement.getAttribute(Constants.ATTR_TARGET_NAMESPACE); + // if the targetNamespace hasn't been defined for the schema use the + // targetNamespace of the definitions element + if(targetNamespace == null || targetNamespace.equals(EMPTY_STRING)) + { + targetNamespace = wsdlDefinition.getTargetNamespace(); + w3celement.setAttribute(Constants.ATTR_TARGET_NAMESPACE,targetNamespace); + } + + // If the namespace given is one of the old schema namespaces produce a warning. + String namespace = w3celement.getNamespaceURI(); + if(namespace.equals(Constants.NS_URI_XSD_1999) || namespace.equals(Constants.NS_URI_XSD_2000)) + { + valInfo.addWarning( + messagegenerator.getString(_WARN_OLD_SCHEMA_NAMESPACE, QUOTE + Constants.NS_URI_XSD_2001 + QUOTE), element); + } + + // now create and call the validator for the inline schema + XSDValidator schemav = new XSDValidator(); + + //String fileLocation = new URL(validatormanager.getFilename()).getPath(); + InlineXSDResolver inlineEntityResolver = + getEntityResolver(wsdlDefinition, (Types) parents.get(0), baseLocation, targetNamespace); + // add in the external XSD Catalog to resolve schemas offline + XMLEntityResolverChain entityResolverChain = new XMLEntityResolverChain(); + entityResolverChain.addEntityResolver(inlineEntityResolver); + entityResolverChain.addEntityResolver((XMLEntityResolver)valInfo.getURIResolver()); + //entityResolverChain.addEntityResolver(XMLCatalogResolver.getInstance()); + entityResolverChain.addEntityResolver(new FileEntityResolver()); + + // Create the string representation of the inline schema. + String xsd = InlineSchemaGenerator.createXSDString(w3celement, elements, baseLocation, parentnamespaces, inlineEntityResolver.getInlineSchemaNSs()); + + + schemav.validateInlineSchema(xsd, targetNamespace, baseLocation, entityResolverChain, inlineEntityResolver); + +// check if the SOAP Encoding namespace is required but not imported + if (InlineSchemaGenerator.soapEncodingRequiredNotImported(elem.getElement(), baseLocation, parentnamespaces)) + { + valInfo.addWarning(messagegenerator.getString(_WARN_SOAPENC_IMPORTED_SCHEMA), element); + } + + // If the schema isn't valid add the error messages produced to valinfo. + // Don't add the errors if they are located on another inline schema. These + // will be reported when the other schema is validated. + + if (!schemav.isValid()) + { + Iterator errors = schemav.getErrors().iterator(); + while (errors.hasNext()) + { + ErrorMessage err = (ErrorMessage) errors.next(); + String uri = err.getURI(); + int line = err.getErrorLine(); + String errmess = err.getErrorMessage(); + errmess = replaceNamespace(errmess, namespace); + if(line > 0) + { + if(uri == null || uri.equals(valInfo.getFileURI())) + { + valInfo.addError(errmess, getObjectAtLine(line - 1, elements)); + } + else if(!inlineEntityResolver.isInlineSchema(uri) && !uri.equals(valInfo.getFileURI() + InlineXSDResolver.INLINE_SCHEMA_ID)) + { + valInfo.addError(errmess, line, err.getErrorColumn(), uri); + } + } + else if(uri != null && !inlineEntityResolver.isInlineSchema(uri) && !uri.equals(valInfo.getFileURI() + InlineXSDResolver.INLINE_SCHEMA_ID)) + { + valInfo.addError(errmess, 0,0, uri); + } + } + } + // if the schema is valid, assign the model to the validatormanager + else + { + XSModel xsModel = schemav.getXSModel(); + valInfo.addSchema(xsModel); + } + } + + /** + * Get an entity resolver that will resolve inline schemas. Every inline schema is preregistered with + * the resolver. + * + * @param wsdlDefinition The WSDL definitions element. + * @param types The types element. + * @param referenceLocation The location of the file that contains this schema. + * @param targetNamespace The targetNamespace of the schema. + * @return An entity resolver that can resolve inline schemas. + */ + protected InlineXSDResolver getEntityResolver(Definition wsdlDefinition, Types types, String referenceLocation, String targetNamespace) + { + InlineXSDResolver entityResolver = new InlineXSDResolver(); +// entityResolver.setReferenceLocation(referenceLocation); + List schemas = types.getExtensibilityElements(); + if (schemas != null) + { + Iterator iSchemas = schemas.iterator(); + Set namespaces = new TreeSet(); + while (iSchemas.hasNext()) + { + UnknownExtensibilityElement extElem = (UnknownExtensibilityElement) iSchemas.next(); + String thisNamespace = extElem.getElement().getAttribute(Constants.ATTR_TARGET_NAMESPACE); + if(thisNamespace != null) + { + namespaces.add(thisNamespace); + } + } + iSchemas = schemas.iterator(); + + while (iSchemas.hasNext()) + { + UnknownExtensibilityElement extElem = (UnknownExtensibilityElement) iSchemas.next(); + String thisNamespace = extElem.getElement().getAttribute(Constants.ATTR_TARGET_NAMESPACE); + if (thisNamespace != null && !thisNamespace.equalsIgnoreCase(targetNamespace)) + { + + Element element = extElem.getElement(); + +// create the inline schema string + //Element w3celement = elem.getElement(); + Hashtable parentnamespaces = getNamespaceDeclarationsFromParents(wsdlDefinition,element); + String xsd = InlineSchemaGenerator.createXSDString(element, new ArrayList(), referenceLocation, parentnamespaces, namespaces); + //addNamespaceDeclarationsFromParents(wsdlDefinition,element); + entityResolver.add(thisNamespace, xsd); + } + + } + } + return entityResolver; + } + + /** + * @see org.eclipse.wst.wsdl.validation.internal.wsdl11.validator.IWSDL11Validator#setResourceBundle(java.util.ResourceBundle) + */ + public void setResourceBundle(ResourceBundle rb) + { + messagegenerator = new MessageGenerator(rb); + } + + public void setMessageGenerator(MessageGenerator messgen) + { + messagegenerator = messgen; + } + + /** + * Get the namespace declarations as in the form + * xmlns="somenamespace" + * from the definitions and types elements and add them to the schema element so the schema + * validator will have access to them. + * + * @param wsdlDefinition The WSDL definitions element. + * @param element The types element. + * @return A hashtable with the namespace elements from the elements provided. + */ + protected Hashtable getNamespaceDeclarationsFromParents(Definition wsdlDefinition, Element element) + { + Hashtable nss = new Hashtable(); + Iterator nameSpaces = wsdlDefinition.getNamespaces().keySet().iterator(); + + String XMLNS = Constants.ATTR_XMLNS; + + String schemaLocation = ""; + while (nameSpaces.hasNext()) + { + String nsprefix = XMLNS; + String ns = (String) nameSpaces.next(); + if (!ns.equalsIgnoreCase("")) + { + nsprefix += ":"; + } + if (!element.hasAttribute(nsprefix + ns)) + { + nss.put(nsprefix + ns, wsdlDefinition.getNamespace(ns)); +// element.setAttribute(nsprefix + ns, wsdlDefinition.getNamespace(ns)); + } + + } + // Next handle the parent types element + NamedNodeMap atts = element.getParentNode().getAttributes(); + int attslength = atts.getLength(); + for (int i = 0; i < attslength; i++) + { + Node tempnode = atts.item(i); + String nodename = tempnode.getNodeName(); + // if this is a namespace attribute + if (nodename.indexOf(XMLNS) != -1) + { + nss.put(nodename, tempnode.getNodeValue()); + //element.setAttribute(nodename, tempnode.getNodeValue()); + } + } + return nss; + } + + /** + * Given a line number for the schema returns the element found on that line. + * Useful for obtaining elements from schema Strings. + * + * @param line The line number for the schema. + * @param elements The list of elements to check. + * @return The object located at the line or at line 0 if the line is invalid. + */ + protected Object getObjectAtLine(int line, List elements) + { + if(line < 0 || line >= elements.size()) + { + line = 0; + } + return elements.get(line); + } + + /** + * Replace any instance of the 2001 schema namespace in the given message with + * the given namespace. + * + * @param message The message to replace the namespace in. + * @param namespace The namespace used for replacement. + * @return The message with the 2001 schema namespace replaced by the given namespace. + */ + private String replaceNamespace(String message, String namespace) + { + String xsd2001 = Constants.NS_URI_XSD_2001; + int start = message.indexOf(xsd2001); + int end = start + xsd2001.length(); + if(start < 0) + { + return message; + } + String startString = message.substring(0,start); + String endString = message.substring(end,message.length()); + return startString + namespace + endString; + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/InlineXSDResolver.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/InlineXSDResolver.java new file mode 100644 index 000000000..5f87b3951 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/InlineXSDResolver.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.Hashtable; +import java.util.Set; + +import org.apache.xerces.xni.XMLResourceIdentifier; +import org.apache.xerces.xni.XNIException; +import org.apache.xerces.xni.parser.XMLEntityResolver; +import org.apache.xerces.xni.parser.XMLInputSource; + +/** + * An XMLEntityResolver that allows inline schemas to resolve each other through imports. + */ +public class InlineXSDResolver implements XMLEntityResolver +{ + public static final String INLINE_SCHEMA_ID = "#inlineschema"; + protected final String FILE_PREFIX = "file:"; + protected final String XMLNS = "xmlns"; + protected Hashtable entities = new Hashtable(); +// protected String refLocation = null; + protected XMLEntityResolver externalResolver = null; + protected XMLInputSource referringSchemaInputSource = null; + protected String referringSchemaNamespace = null; + + /** + * Constuctor. + */ + public InlineXSDResolver() + { + } + + /* (non-Javadoc) + * @see org.apache.xerces.xni.parser.XMLEntityResolver#resolveEntity(org.apache.xerces.xni.XMLResourceIdentifier) + */ + public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) + throws XNIException, IOException { + String systemId = resourceIdentifier.getExpandedSystemId(); + String publicId = resourceIdentifier.getPublicId(); + String namespace = resourceIdentifier.getNamespace(); + XMLInputSource is = null; + Reader reader = null; + String schema = null; + if (systemId == null) + { + if(publicId == null) + { + if(namespace == null) + { + return null; + } + else + { + systemId = namespace; + } + } + else + { + systemId = publicId; + } + } + + if(referringSchemaNamespace != null && referringSchemaNamespace.equals(systemId)) + { + if(referringSchemaInputSource!=null) + { + return referringSchemaInputSource; + } + } + else if ((schema = (String) entities.get(systemId)) != null && !schema.equals("")) + { + is = new XMLInputSource(publicId, referringSchemaInputSource.getSystemId() + INLINE_SCHEMA_ID, null, new StringReader(schema),null); + } + + //if(is == null) + //{ + // throw new IOException(); + //} + return is; + } + + /** + * Add an inline schema. + * + * @param targetNamespace - the target namespace of the schema + * @param schema - a string representation of the schema + */ + public void add(String targetNamespace, String schema) + { + entities.put(targetNamespace, schema); + } + + /** + * Add the referring inline schema. + * + * @param inputSource - a representation of the inline schema + * @param namespace - the namespace of the inline schema + */ + public void addReferringSchema(XMLInputSource inputSource, String namespace) + { + referringSchemaInputSource = inputSource; + referringSchemaNamespace = namespace; + } + + /** + * Return true if the namespace corresponds to an inline schema, false otherwise. + * + * @param namespace The namespace of the schema. + * @return True if the namespace corresponds to an inline schema, false otherwise. + */ + public boolean isInlineSchema(String namespace) + { + return entities.containsKey(namespace); + } + + /** + * Get the set of the inline schema namespaces. + * + * @return The set of the inline schema namespaces. + */ + public Set getInlineSchemaNSs() + { + return entities.keySet(); + } + +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/SchemaAttributeTable.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/SchemaAttributeTable.java new file mode 100644 index 000000000..fe43e121c --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/SchemaAttributeTable.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd; + +import org.apache.xerces.impl.xs.SchemaSymbols; +import org.apache.xerces.util.SymbolTable; +/** + * This class will allow the calling code to see if the attribute is defined in schema. + * This serves as schema for schema. + */ +public class SchemaAttributeTable extends SymbolTable +{ + + /** + * Constructor. + */ + public SchemaAttributeTable() + { + // add all of the sybols to the table. SchemaSymbols probably should have + // a SymbolTable for these but it doesn't + + super.addSymbol(SchemaSymbols.ATTVAL_TWOPOUNDANY); + super.addSymbol(SchemaSymbols.ATTVAL_TWOPOUNDLOCAL); + super.addSymbol(SchemaSymbols.ATTVAL_TWOPOUNDOTHER); + super.addSymbol(SchemaSymbols.ATTVAL_TWOPOUNDTARGETNS); + super.addSymbol(SchemaSymbols.ATTVAL_POUNDALL); + super.addSymbol(SchemaSymbols.ATTVAL_FALSE_0); + super.addSymbol(SchemaSymbols.ATTVAL_TRUE_1); + super.addSymbol(SchemaSymbols.ATTVAL_ANYSIMPLETYPE); + super.addSymbol(SchemaSymbols.ATTVAL_ANYTYPE); + super.addSymbol(SchemaSymbols.ATTVAL_ANYURI); + super.addSymbol(SchemaSymbols.ATTVAL_BASE64BINARY); + super.addSymbol(SchemaSymbols.ATTVAL_BOOLEAN); + super.addSymbol(SchemaSymbols.ATTVAL_BYTE); + super.addSymbol(SchemaSymbols.ATTVAL_COLLAPSE); + super.addSymbol(SchemaSymbols.ATTVAL_DATE); + super.addSymbol(SchemaSymbols.ATTVAL_DATETIME); + super.addSymbol(SchemaSymbols.ATTVAL_DAY); + super.addSymbol(SchemaSymbols.ATTVAL_DECIMAL); + super.addSymbol(SchemaSymbols.ATTVAL_DOUBLE); + super.addSymbol(SchemaSymbols.ATTVAL_DURATION); + super.addSymbol(SchemaSymbols.ATTVAL_ENTITY); + super.addSymbol(SchemaSymbols.ATTVAL_ENTITIES); + super.addSymbol(SchemaSymbols.ATTVAL_EXTENSION); + super.addSymbol(SchemaSymbols.ATTVAL_FALSE); + super.addSymbol(SchemaSymbols.ATTVAL_FLOAT); + super.addSymbol(SchemaSymbols.ATTVAL_HEXBINARY); + super.addSymbol(SchemaSymbols.ATTVAL_ID); + super.addSymbol(SchemaSymbols.ATTVAL_IDREF); + super.addSymbol(SchemaSymbols.ATTVAL_IDREFS); + super.addSymbol(SchemaSymbols.ATTVAL_INT); + super.addSymbol(SchemaSymbols.ATTVAL_INTEGER); + super.addSymbol(SchemaSymbols.ATTVAL_LANGUAGE); + super.addSymbol(SchemaSymbols.ATTVAL_LAX); + super.addSymbol(SchemaSymbols.ATTVAL_LIST); + super.addSymbol(SchemaSymbols.ATTVAL_LONG); + super.addSymbol(SchemaSymbols.ATTVAL_NAME); + super.addSymbol(SchemaSymbols.ATTVAL_NEGATIVEINTEGER); + super.addSymbol(SchemaSymbols.ATTVAL_MONTH); + super.addSymbol(SchemaSymbols.ATTVAL_MONTHDAY); + super.addSymbol(SchemaSymbols.ATTVAL_NCNAME); + super.addSymbol(SchemaSymbols.ATTVAL_NMTOKEN); + super.addSymbol(SchemaSymbols.ATTVAL_NMTOKENS); + super.addSymbol(SchemaSymbols.ATTVAL_NONNEGATIVEINTEGER); + super.addSymbol(SchemaSymbols.ATTVAL_NONPOSITIVEINTEGER); + super.addSymbol(SchemaSymbols.ATTVAL_NORMALIZEDSTRING); + super.addSymbol(SchemaSymbols.ATTVAL_NOTATION); + super.addSymbol(SchemaSymbols.ATTVAL_OPTIONAL); + super.addSymbol(SchemaSymbols.ATTVAL_POSITIVEINTEGER); + super.addSymbol(SchemaSymbols.ATTVAL_PRESERVE); + super.addSymbol(SchemaSymbols.ATTVAL_PROHIBITED); + super.addSymbol(SchemaSymbols.ATTVAL_QNAME); + super.addSymbol(SchemaSymbols.ATTVAL_QUALIFIED); + super.addSymbol(SchemaSymbols.ATTVAL_REPLACE); + super.addSymbol(SchemaSymbols.ATTVAL_REQUIRED); + super.addSymbol(SchemaSymbols.ATTVAL_RESTRICTION); + super.addSymbol(SchemaSymbols.ATTVAL_SHORT); + super.addSymbol(SchemaSymbols.ATTVAL_SKIP); + super.addSymbol(SchemaSymbols.ATTVAL_STRICT); + super.addSymbol(SchemaSymbols.ATTVAL_STRING); + super.addSymbol(SchemaSymbols.ATTVAL_SUBSTITUTION); + super.addSymbol(SchemaSymbols.ATTVAL_TIME); + super.addSymbol(SchemaSymbols.ATTVAL_TOKEN); + super.addSymbol(SchemaSymbols.ATTVAL_TRUE); + super.addSymbol(SchemaSymbols.ATTVAL_UNBOUNDED); + super.addSymbol(SchemaSymbols.ATTVAL_UNION); + super.addSymbol(SchemaSymbols.ATTVAL_UNQUALIFIED); + super.addSymbol(SchemaSymbols.ATTVAL_UNSIGNEDBYTE); + super.addSymbol(SchemaSymbols.ATTVAL_UNSIGNEDINT); + super.addSymbol(SchemaSymbols.ATTVAL_UNSIGNEDLONG); + super.addSymbol(SchemaSymbols.ATTVAL_UNSIGNEDSHORT); + super.addSymbol(SchemaSymbols.ATTVAL_YEAR); + super.addSymbol(SchemaSymbols.ATTVAL_YEARMONTH); + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/ValidateErrorHandler.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/ValidateErrorHandler.java new file mode 100644 index 000000000..fda40a4d9 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/ValidateErrorHandler.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.xerces.xni.XNIException; +import org.apache.xerces.xni.parser.XMLErrorHandler; +import org.apache.xerces.xni.parser.XMLParseException; +import org.eclipse.wst.wsdl.validation.internal.util.ErrorMessage; + +/** + * An implementation of XMLErrorHandler that captures error from Xerces. + */ +public class ValidateErrorHandler implements XMLErrorHandler +{ + ArrayList errorList = new ArrayList(); + + + + /** + * Get the error messages created by Xerces. + * + * @return The errors list. + */ + public List getErrorMessages() + { + return errorList; + } + + /** + * Create a validation message from the exception and severity. + * + * @param error The error. + * @param severity The severity. + * @return An error message. + */ + protected ErrorMessage createValidationMessageForException(XMLParseException error, int severity) + { + String uri = error.getLiteralSystemId(); + if(uri == null) + { + uri = error.getPublicId(); + } + ErrorMessage errorMessage = new ErrorMessage(); + errorMessage.setErrorLine(error.getLineNumber()); + errorMessage.setErrorMessage(error.getMessage()); + errorMessage.setErrorColumn(error.getColumnNumber()); + errorMessage.setURI(uri); + errorMessage.setSeverity(severity); + return errorMessage; + } + + /** + * @see org.apache.xerces.xni.parser.XMLErrorHandler#error(java.lang.String, java.lang.String, org.apache.xerces.xni.parser.XMLParseException) + */ + public void error(String arg0, String arg1, XMLParseException exception) throws XNIException + { + errorList.add(createValidationMessageForException(exception, DOMError.SEVERITY_ERROR)); + } + + /** + * @see org.apache.xerces.xni.parser.XMLErrorHandler#fatalError(java.lang.String, java.lang.String, org.apache.xerces.xni.parser.XMLParseException) + */ + public void fatalError(String arg0, String arg1, XMLParseException exception) throws XNIException + { + errorList.add(createValidationMessageForException(exception, DOMError.SEVERITY_FATAL_ERROR)); + } + + /** + * @see org.apache.xerces.xni.parser.XMLErrorHandler#warning(java.lang.String, java.lang.String, org.apache.xerces.xni.parser.XMLParseException) + */ + public void warning(String arg0, String arg1, XMLParseException exception) throws XNIException + { + errorList.add(createValidationMessageForException(exception, DOMError.SEVERITY_WARNING)); + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/XMLEntityResolverChain.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/XMLEntityResolverChain.java new file mode 100644 index 000000000..cf936152a --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/XMLEntityResolverChain.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +import org.apache.xerces.xni.XMLResourceIdentifier; +import org.apache.xerces.xni.XNIException; +import org.apache.xerces.xni.parser.XMLEntityResolver; +import org.apache.xerces.xni.parser.XMLInputSource; + +/** + * Handle a chain of entity resolvers. + */ +public class XMLEntityResolverChain implements XMLEntityResolver +{ + private List entityResolvers = new Vector(); + + /** + * @see org.apache.xerces.xni.parser.XMLEntityResolver#resolveEntity(org.apache.xerces.xni.XMLResourceIdentifier) + */ + public XMLInputSource resolveEntity(XMLResourceIdentifier xmlResourceIdentifier) throws XNIException, IOException + { + XMLInputSource is = null; + Iterator entityResolverIter = entityResolvers.iterator(); + while (entityResolverIter.hasNext()) + { + XMLEntityResolver entityResolver = (XMLEntityResolver)entityResolverIter.next(); + try + { + is = entityResolver.resolveEntity(xmlResourceIdentifier); + } + catch (XNIException e) + { + } + catch (IOException e) + { + } + if (is != null) + { + break; + } + } + // Throw and IOException if the file isn't found + if (is == null) + { + throw new IOException("Unable to locate file"); + } + return is; + } + + /** + * Add an entity resolver to this chain. + * + * @param entityResolver The resolver to add to the chain. + */ + public void addEntityResolver(XMLEntityResolver entityResolver) + { + entityResolvers.add(entityResolver); + } +} diff --git a/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/XSDValidator.java b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/XSDValidator.java new file mode 100644 index 000000000..1ed97f647 --- /dev/null +++ b/bundles/org.eclipse.wst.wsdl.validation/src/org/eclipse/wst/wsdl/validation/internal/wsdl11/xsd/XSDValidator.java @@ -0,0 +1,206 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd; + +import java.io.Reader; +import java.io.StringReader; +import java.util.List; + +import org.apache.xerces.parsers.XMLGrammarPreparser; +import org.apache.xerces.util.XMLGrammarPoolImpl; +import org.apache.xerces.xni.grammars.XMLGrammarDescription; +import org.apache.xerces.xni.grammars.XMLGrammarLoader; +import org.apache.xerces.xni.grammars.XMLGrammarPool; +import org.apache.xerces.xni.grammars.XSGrammar; +import org.apache.xerces.xni.parser.XMLEntityResolver; +import org.apache.xerces.xni.parser.XMLInputSource; +import org.apache.xerces.xs.XSModel; + + +/** + * Validate schemas from files or String. + */ +public class XSDValidator +{ + protected final String NAMESPACES_FEATURE_ID = "http://xml.org/sax/features/namespaces"; + protected final String VALIDATION_FEATURE_ID = "http://xml.org/sax/features/validation"; + protected final String SCHEMA_VALIDATION_FEATURE_ID = "http://apache.org/xml/features/validation/schema"; + protected final String SCHEMA_FULL_CHECKING_FEATURE_ID = + "http://apache.org/xml/features/validation/schema-full-checking"; + protected final String CONTINUE_AFTER_FATAL_ERROR_ID = "http://apache.org/xml/features/continue-after-fatal-error"; + protected final String FILE_PREFIX = "file:"; + protected final String XMLNS = "xmlns"; + protected final String TARGETNAMESPACE = "targetNamespace"; + protected final String NAMESPACE = "namespace"; + protected final String IMPORT = "import"; + protected final String SCHEMALOCATION = "schemaLocation"; + protected final String TYPE = "type"; + protected final String[] ignoreNamespaces = + { "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/1999/XMLSchema" }; + + protected XSModel xsModel; + protected boolean isValidXSD; + protected List errors; + protected String filelocation; + + /** + * Constructor. + */ + public XSDValidator() + { + xsModel = null; + isValidXSD = false; + errors = null; + } + /** + * Validate an inline schema. + * + * @param schema A schema represented as a string. + * @param targetNamespace The target namespace of the schema. + * @param fileloc The uri of the file that contains the schema. + */ + public void validateInlineSchema(String schema, String targetNamespace, String fileloc) + { + validateInlineSchema(schema, targetNamespace, fileloc, null, null); + } + + /** + * Validate an inline schema and specify an entity resolver. + * + * @param schema This schema represented as a string. + * @param targetNamespace The target namespace of the schema. + * @param fileloc The uri of the file that contains this schema. + * @param entityResolverChain The entity resolver chain. + * @param inlineSchemaEntityResolver An inline schema resolver for this schema. + */ + public void validateInlineSchema( + String schema, + String targetNamespace, + String fileloc, + XMLEntityResolver entityResolverChain, + XMLEntityResolver inlineSchemaEntityResolver) + { + filelocation = fileloc; + + validateXSD(schema, true, entityResolverChain,targetNamespace, inlineSchemaEntityResolver); + } + + /** + * Validate the file located at the uri specified with the given entity resolver. + * + * @param uri An absolute uri for the schema location. + * @param entityResolver An entity resolver to be used. + */ + public void validate(String uri, XMLEntityResolver entityResolver) + { + validateXSD(uri, false, entityResolver, null, null); + } + + /** + * Validate the schema. + * + * @param schema The schema or it's location. + * @param inlineXSD True if an inline schema, false otherwise. + * @param entityResolver An entity resolver to use. + * @param targetNamespace The target namespace of the schema being validated. + * @param inlineSchemaEntityResolver An inline schema entity resolver. + */ + protected void validateXSD(String schema, boolean inlineXSD, XMLEntityResolver entityResolver, String targetNamespace, XMLEntityResolver inlineSchemaEntityResolver) + { + ValidateErrorHandler errorHandler = new ValidateErrorHandler(); + errorHandler.getErrorMessages().clear(); + try + { + XMLGrammarPreparser grammarPreparser = new XMLGrammarPreparser(); + XMLGrammarPool grammarPool = new XMLGrammarPoolImpl(); + grammarPreparser.setGrammarPool(grammarPool); + + grammarPreparser.setErrorHandler(errorHandler); + if (entityResolver != null) + { + grammarPreparser.setEntityResolver(entityResolver); + } + + try + { + XMLInputSource is = null; + // this allows support for the inline schema in WSDL documents + if (inlineXSD) + { + + Reader reader = new StringReader(schema); + is = new XMLInputSource(null,filelocation,filelocation,reader,null); + + ((InlineXSDResolver)inlineSchemaEntityResolver).addReferringSchema(is,targetNamespace); + + } + // get the input source for an external schema file + else + { + is = new XMLInputSource(null,schema,schema); + } + + grammarPreparser.registerPreparser(XMLGrammarDescription.XML_SCHEMA,null/*schemaLoader*/); + XMLGrammarLoader schemaLoader = grammarPreparser.getLoader(XMLGrammarDescription.XML_SCHEMA); + + XSGrammar grammar = (XSGrammar)grammarPreparser.preparseGrammar(XMLGrammarDescription.XML_SCHEMA,is); + xsModel = grammar.toXSModel(); + } + catch (Exception e) + { + //parser will return null pointer exception if the document is structurally invalid + //TODO: log error message + System.out.println(e); + } + + errors = errorHandler.getErrorMessages(); + } + catch (Exception e) + { + System.out.println(e); + } + if (errors.isEmpty()) + { + isValidXSD = true; + } + } + + /** + * Returns the XSModel created. + * + * @return The XSModel created. + */ + + public XSModel getXSModel() + { + return xsModel; + } + /** + * Returns true if the schema is valid, false otherwise. + * + * @return True if the schema is valid, false otherwise. + */ + public boolean isValid() + { + return isValidXSD; + } + + /** + * Return the error list. + * + * @return A list of error from the schema. + */ + public List getErrors() + { + return errors; + } +} |