/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.yoko.tools.processors.idl;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.wsdl.Binding;
import javax.wsdl.Definition;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import antlr.collections.AST;
import org.apache.schemas.yoko.bindings.corba.BindingType;
import org.apache.schemas.yoko.bindings.corba.Object;
import org.apache.schemas.yoko.bindings.corba.TypeMappingType;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaAnnotation;
import org.apache.ws.commons.schema.XmlSchemaAppInfo;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaImport;
import org.apache.ws.commons.schema.XmlSchemaObjectCollection;
import org.apache.ws.commons.schema.XmlSchemaType;
import org.apache.ws.commons.schema.utils.NamespaceMap;
import org.apache.yoko.tools.common.ReferenceConstants;
import org.apache.yoko.wsdl.CorbaTypeImpl;
public class ObjectReferenceVisitor extends VisitorBase {
private static boolean declaredWSAImport;
private Definition wsdlDefinition;
public ObjectReferenceVisitor(Scope scope,
XmlSchemaCollection xmlSchemas,
XmlSchema xmlSchema,
TypeMappingType typeMappingType,
Definition wsdlDef) {
super(scope, xmlSchemas, xmlSchema, typeMappingType);
wsdlDefinition = wsdlDef;
}
public static boolean accept(AST node) {
if (node.getType() == IDLTokenTypes.LITERAL_Object
|| node.getType() == IDLTokenTypes.IDENT) {
return true;
}
return false;
}
public void visit(AST node) {
if (!declaredWSAImport) {
// We need to add an import statement to include the WS addressing types
XmlSchemaImport wsaImport = new XmlSchemaImport();
wsaImport.setNamespace(ReferenceConstants.WSADDRESSING_NAMESPACE);
wsaImport.setSchemaLocation(ReferenceConstants.WSADDRESSING_LOCATION);
schema.getItems().add(wsaImport);
// Add the addressing namespace to the WSDLs list of namespaces.
wsdlDefinition.addNamespace(ReferenceConstants.WSADDRESSING_PREFIX,
ReferenceConstants.WSADDRESSING_NAMESPACE);
try {
// This is used to get the correct prefix in the schema section of
// the wsdl. If we don't have this, then this namespace gets an
// arbitrary prefix (e.g. ns5 instead of wsa).
NamespaceMap nsMap = (NamespaceMap)schema.getNamespaceContext();
if (nsMap == null) {
nsMap = new NamespaceMap();
nsMap.add(ReferenceConstants.WSADDRESSING_PREFIX,
ReferenceConstants.WSADDRESSING_NAMESPACE);
schema.setNamespaceContext(nsMap);
} else {
nsMap.add(ReferenceConstants.WSADDRESSING_PREFIX,
ReferenceConstants.WSADDRESSING_NAMESPACE);
}
} catch (ClassCastException ex) {
// Consume the exception. It is still OK with the default prefix,
// just not as clear.
}
}
declaredWSAImport = true;
// There are two types of object references we can encounter. Each one
// requires us to do something differnt so we'll have methods for each
// type.
if (node.getType() == IDLTokenTypes.LITERAL_Object) {
visitDefaultTypeObjectReference(node);
} else {
// This should be of type IDENT
visitCustomTypeObjectReference(node);
}
}
private void visitDefaultTypeObjectReference(AST node) {
// Even though we don't need to add a schema definition for a default endpoint
// type, we still need to create a schema type so that the visitor knows what
// kind of parameter this is. For a default endpoint, we'll just provide a
// reference to a WS addressing EndpointReferenceType.
XmlSchema wsaSchema = new XmlSchema(ReferenceConstants.WSADDRESSING_NAMESPACE, schemas);
XmlSchemaType objectType = new XmlSchemaType(wsaSchema);
objectType.setName(ReferenceConstants.WSADDRESSING_LOCAL_NAME);
setSchemaType(objectType);
// Build and assign the corba:object to the visitor
Object corbaObject = new Object();
corbaObject.setBinding(new QName(""));
corbaObject.setQName(new QName(typeMap.getTargetNamespace(), "CORBA.Object"));
corbaObject.setRepositoryID("IDL:omg.org/CORBA/Object/1.0");
corbaObject.setType(objectType.getQName());
setCorbaType(corbaObject);
// Add the object definition to the typemap. We only need to add the default
// type once.
if (!isReferenceCORBATypeDefined(corbaObject.getQName())) {
typeMap.getStructOrExceptionOrUnion().add(corbaObject);
}
}
private void visitCustomTypeObjectReference(AST node) {
Scope customScope = new Scope(new Scope(), node);
QName referenceName = new QName(schema.getTargetNamespace(),
customScope.toString() + "Ref");
String repositoryID = customScope.toIDLRepositoryID();
QName bindingName = getBindingQNameByID(repositoryID);
if (bindingName == null) {
// We need to have a binding for this kind of object reference to work
throw new RuntimeException("[ObjectReferenceVisitor: No binding available for endpoint]");
}
// Create a schema namespace for WS addressing and use it to create an endpoint
// reference type. This will be used as the type for our endpoint reference.
XmlSchema wsaSchema = new XmlSchema(ReferenceConstants.WSADDRESSING_NAMESPACE, schemas);
XmlSchemaType wsaType = new XmlSchemaType(wsaSchema);
wsaType.setName(ReferenceConstants.WSADDRESSING_LOCAL_NAME);
// Check to see if we have already defined an element for this reference type. If
// we have, then there is no need to add it to the schema again.
if (!isReferenceSchemaTypeDefined(referenceName)) {
// We need to add a new element definition to the schema section of our WSDL.
// For custom endpoint types, this should contain an annotation which points
// to the binding which will be used for this endpoint type.
XmlSchemaElement refElement = new XmlSchemaElement();
refElement.setQName(referenceName);
refElement.setName(referenceName.getLocalPart());
refElement.setSchemaType(wsaType);
refElement.setSchemaTypeName(wsaType.getQName());
// Create an annotation which contains the CORBA binding for the element
XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
XmlSchemaAppInfo appInfo = new XmlSchemaAppInfo();
try {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.newDocument();
Element el = doc.createElement("appinfo");
el.setTextContent("corba:binding=" + bindingName.getLocalPart());
// TODO: This is correct but the appinfo markup is never added to the
// schema. Investigate.
appInfo.setMarkup(el.getChildNodes());
} catch (ParserConfigurationException ex) {
throw new RuntimeException("[ObjectReferenceVisitor: error creating endpoint schema]");
}
annotation.getItems().add(appInfo);
refElement.setAnnotation(annotation);
schema.getElements().add(referenceName, refElement);
schema.getItems().add(refElement);
}
// A schema type is required for visitors. Create one based on the element we just added.
XmlSchemaType endpointType = new XmlSchemaType(schema);
endpointType.setName(referenceName.getLocalPart());
setSchemaType(endpointType);
// Build and assign the corba:object to the visitor
Object corbaObject = new Object();
corbaObject.setBinding(bindingName);
corbaObject.setQName(new QName(typeMap.getTargetNamespace(), customScope.toString()));
corbaObject.setRepositoryID(repositoryID);
corbaObject.setType(wsaType.getQName());
setCorbaType(corbaObject);
// Add the object definition to the typemap. We only need to add the default
// type once.
if (!isReferenceCORBATypeDefined(corbaObject.getQName())) {
typeMap.getStructOrExceptionOrUnion().add(corbaObject);
}
}
private boolean isReferenceCORBATypeDefined(QName objectReferenceName) {
// Get the list of all corba types already defined and look for the provided
// QName. If we have defined this type, we don't need to add it to the typemap
// again.
List<CorbaTypeImpl> allTypes = typeMap.getStructOrExceptionOrUnion();
for (Iterator<CorbaTypeImpl> iter = allTypes.iterator(); iter.hasNext();) {
CorbaTypeImpl impl = iter.next();
if (impl.getQName().equals(objectReferenceName)) {
return true;
}
}
return false;
}
private boolean isReferenceSchemaTypeDefined(QName objectReferenceName) {
XmlSchemaObjectCollection schemaObjects = schema.getItems();
for (Iterator iter = schemaObjects.getIterator(); iter.hasNext();) {
java.lang.Object schemaObj = iter.next();
if (schemaObj instanceof XmlSchemaElement) {
XmlSchemaElement el = (XmlSchemaElement)schemaObj;
if (el.getName().equals(objectReferenceName.getLocalPart())) {
return true;
}
}
}
return false;
}
private QName getBindingQNameByID(String repositoryID) {
// We need to find the binding which corresponds with the given repository ID.
// This is specified in the schema definition for a custom endpoint
// reference type.
Collection bindings = wsdlDefinition.getBindings().values();
for (Iterator iter = bindings.iterator(); iter.hasNext();) {
Binding b = (Binding)iter.next();
List extElements = b.getExtensibilityElements();
for (Iterator extIter = extElements.iterator(); extIter.hasNext();) {
java.lang.Object element = extIter.next();
if (element instanceof BindingType) {
BindingType bt = (BindingType)element;
if (bt.getRepositoryID().equals(repositoryID)) {
return b.getQName();
}
}
}
}
return null;
}
}