| /* Copyright 2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.xmlbeans.impl.marshal; |
| |
| import org.apache.xmlbeans.EncodingStyle; |
| import org.apache.xmlbeans.SoapMarshaller; |
| import org.apache.xmlbeans.XmlException; |
| import org.apache.xmlbeans.XmlOptions; |
| import org.apache.xmlbeans.XmlRuntimeException; |
| import org.apache.xmlbeans.impl.binding.bts.BindingLoader; |
| import org.apache.xmlbeans.impl.binding.bts.BindingType; |
| import org.apache.xmlbeans.impl.marshal.util.ArrayUtils; |
| import org.apache.xmlbeans.impl.marshal.util.collections.EmptyIterator; |
| |
| import javax.xml.namespace.NamespaceContext; |
| import javax.xml.namespace.QName; |
| import javax.xml.stream.XMLStreamReader; |
| import javax.xml.stream.XMLStreamWriter; |
| import java.util.Iterator; |
| |
| //this class is not thread safe and doesn't have to be per javadocs |
| |
| final class SoapMarshallerImpl |
| implements SoapMarshaller |
| { |
| //per binding context constants |
| private final BindingLoader loader; |
| private final RuntimeBindingTypeTable typeTable; |
| private final EncodingStyle encodingStyle; |
| private final InstanceVisitor instanceVisitor = new InstanceVisitor(); |
| |
| private ObjectRefTable objectRefTable; |
| |
| |
| SoapMarshallerImpl(BindingLoader loader, |
| RuntimeBindingTypeTable typeTable, |
| EncodingStyle encodingStyle) |
| { |
| this.loader = loader; |
| this.typeTable = typeTable; |
| this.encodingStyle = encodingStyle; |
| } |
| |
| |
| public void marshalType(XMLStreamWriter writer, |
| Object obj, |
| QName elementName, |
| QName schemaType, |
| String javaType, |
| XmlOptions options) |
| throws XmlException |
| { |
| final RuntimeGlobalProperty prop = |
| createGlobalProperty(schemaType, javaType, elementName, obj); |
| |
| if (prop.getRuntimeBindingType().hasElementChildren()) { |
| objectRefTable = new ObjectRefTable(); |
| } |
| |
| final PushSoapMarshalResult result = |
| createPushSoapResult(writer, options); |
| |
| RuntimeBindingType actual_rtt = prop.getActualRuntimeType(obj, result); |
| addObjectGraphToRefTable(obj, prop, actual_rtt, result); |
| |
| result.marshalType(obj, prop, actual_rtt); |
| |
| } |
| |
| private RuntimeGlobalProperty createGlobalProperty(QName schemaType, String javaType, QName elementName, Object obj) throws XmlException |
| { |
| final BindingType btype = |
| MarshallerImpl.lookupBindingType(schemaType, javaType, |
| elementName, obj, loader); |
| |
| assert btype != null; |
| |
| final RuntimeBindingType runtime_type = |
| typeTable.createRuntimeType(btype, loader); |
| |
| runtime_type.checkInstance(obj); |
| |
| RuntimeGlobalProperty prop = |
| new RuntimeGlobalProperty(elementName, runtime_type); |
| return prop; |
| } |
| |
| private PushSoapMarshalResult createPushSoapResult(XMLStreamWriter writer, |
| XmlOptions options) |
| throws XmlException |
| { |
| final PushSoapMarshalResult result; |
| if (EncodingStyle.SOAP11.equals(encodingStyle)) { |
| result = new PushSoap11MarshalResult(loader, |
| typeTable, |
| writer, |
| options, |
| objectRefTable); |
| } else if (EncodingStyle.SOAP12.equals(encodingStyle)) { |
| throw new AssertionError("UNIMP"); |
| } else { |
| throw new AssertionError("UNKNOWN ENCODING: " + encodingStyle); |
| } |
| return result; |
| } |
| |
| public void marshalReferenced(XMLStreamWriter writer, |
| XmlOptions options) |
| throws XmlException |
| { |
| if (objectRefTable == null || !objectRefTable.hasMultiplyRefdObjects()) |
| return; |
| |
| final PushSoapMarshalResult result = |
| createPushSoapResult(writer, options); |
| result.writeIdParts(); |
| } |
| |
| public XMLStreamReader marshalType(Object obj, |
| QName elementName, |
| QName schemaType, |
| String javaType, |
| XmlOptions options) |
| throws XmlException |
| { |
| final RuntimeGlobalProperty prop = |
| createGlobalProperty(schemaType, javaType, elementName, obj); |
| |
| if (prop.getRuntimeBindingType().hasElementChildren()) { |
| objectRefTable = new ObjectRefTable(); |
| } |
| |
| final NamespaceContext nscontext = |
| MarshallerImpl.getNamespaceContextFromOptions(options); |
| final PullSoapMarshalResult retval = |
| createPullMarshalResult(nscontext, prop, obj, options, false); |
| |
| addObjectGraphToRefTable(obj, prop, |
| prop.getActualRuntimeType(obj, retval), |
| retval); |
| |
| return retval; |
| } |
| |
| private PullSoapMarshalResult createPullMarshalResult(NamespaceContext nscontext, |
| RuntimeBindingProperty prop, |
| Object obj, |
| XmlOptions options, |
| boolean doing_id_parts) |
| throws XmlException |
| { |
| final PullSoapMarshalResult retval; |
| |
| if (EncodingStyle.SOAP11.equals(encodingStyle)) { |
| retval = new Soap11MarshalResult(loader, typeTable, |
| nscontext, prop, obj, options, |
| objectRefTable, doing_id_parts); |
| } else if (EncodingStyle.SOAP12.equals(encodingStyle)) { |
| throw new AssertionError("UNIMP"); |
| } else { |
| throw new AssertionError("UNKNOWN ENCODING: " + encodingStyle); |
| } |
| return retval; |
| } |
| |
| private void addObjectGraphToRefTable(Object obj, |
| RuntimeBindingProperty prop, |
| RuntimeBindingType actual_rtt, |
| MarshalResult result) |
| throws XmlException |
| { |
| if (objectRefTable == null) return; |
| |
| //traverse graph... |
| instanceVisitor.setCurrObject(obj, prop); |
| instanceVisitor.setMarshalResult(result); |
| actual_rtt.accept(instanceVisitor); |
| } |
| |
| public Iterator marshalReferenced(XmlOptions options) |
| throws XmlException |
| { |
| if (objectRefTable == null || !objectRefTable.hasMultiplyRefdObjects()) |
| return EmptyIterator.getInstance(); |
| |
| return new ReaderIterator(options); |
| } |
| |
| private final class ReaderIterator |
| implements Iterator |
| { |
| private final XmlOptions options; |
| private final NamespaceContext nscontext; |
| private final Iterator tblItr = objectRefTable.getMultipleRefTableEntries(); |
| private PullSoapMarshalResult marshalResult; |
| |
| public ReaderIterator(XmlOptions options) |
| { |
| this.options = options; |
| nscontext = MarshallerImpl.getNamespaceContextFromOptions(options); |
| } |
| |
| public boolean hasNext() |
| { |
| return tblItr.hasNext(); |
| } |
| |
| public Object next() |
| { |
| final ObjectRefTable.Value cur_val = |
| (ObjectRefTable.Value)tblItr.next(); |
| assert cur_val.getCnt() > 1; |
| |
| try { |
| if (marshalResult == null) { |
| marshalResult = createPullMarshalResult(nscontext, |
| cur_val.getProp(), |
| cur_val.object, |
| options, true); |
| } else { |
| marshalResult.reset(cur_val.getProp(), cur_val.object, true); |
| } |
| return marshalResult; |
| } |
| catch (XmlException e) { |
| //TODO: REVIEW: is there a better way to do this? |
| //maybe write out own typesafe iterator... |
| throw new XmlRuntimeException(e); |
| } |
| |
| } |
| |
| public void remove() |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| } |
| |
| |
| //NOTE that each call into a visit method should be done with |
| //the actual runtime type |
| |
| private final class InstanceVisitor |
| implements RuntimeTypeVisitor |
| { |
| private Object currObject; |
| private RuntimeBindingProperty currProp; |
| private MarshalResult marshalResult; |
| |
| public void setCurrObject(Object obj, |
| RuntimeBindingProperty prop) |
| { |
| this.currObject = obj; |
| this.currProp = prop; |
| } |
| |
| public void setMarshalResult(MarshalResult marshalResult) |
| { |
| this.marshalResult = marshalResult; |
| } |
| |
| public void visit(BuiltinRuntimeBindingType builtinRuntimeBindingType) |
| throws XmlException |
| { |
| firstVisit(currObject, currProp, builtinRuntimeBindingType); |
| //no element children so we are finished. |
| } |
| |
| public void visit(AnyTypeRuntimeBindingType builtinRuntimeBindingType) |
| throws XmlException |
| { |
| if (currObject == null) |
| return; |
| |
| //we didn't find anything better than the any type |
| final String msg = "unknown type: " + currObject.getClass() + |
| " for property " + currProp; |
| marshalResult.addError(msg); |
| return; |
| } |
| |
| public void visit(ByNameRuntimeBindingType byNameRuntimeBindingType) |
| throws XmlException |
| { |
| final Object curr_obj = currObject; |
| final RuntimeBindingProperty curr_prop = currProp; |
| |
| if (firstVisit(curr_obj, curr_prop, byNameRuntimeBindingType)) { |
| return; |
| } |
| |
| final int elem_cnt = |
| byNameRuntimeBindingType.getElementPropertyCount(); |
| for (int i = 0; i < elem_cnt; i++) { |
| final RuntimeBindingProperty prop = |
| byNameRuntimeBindingType.getElementProperty(i); |
| |
| if (prop.getRuntimeBindingType().isJavaPrimitive()) continue; |
| if (!prop.isSet(curr_obj, marshalResult)) continue; |
| |
| final Object prop_val = prop.getValue(curr_obj, marshalResult); |
| if (prop.isMultiple()) { |
| //NOTE: we don't add the array itself becuase there is no |
| //actual element on which to put the id or href attribute. |
| final Iterator itr = ArrayUtils.getCollectionIterator(prop_val); |
| while (itr.hasNext()) { |
| visitProp(itr.next(), prop, curr_obj, curr_prop); |
| } |
| } else { |
| visitProp(prop_val, prop, curr_obj, curr_prop); |
| } |
| } |
| } |
| |
| private void visitProp(final Object prop_val, |
| final RuntimeBindingProperty prop, |
| final Object curr_obj, |
| final RuntimeBindingProperty curr_prop) |
| throws XmlException |
| { |
| setCurrObject(prop_val, prop); |
| RuntimeBindingType rtt = prop.getRuntimeBindingType(); |
| if (rtt.hasElementChildren()) { |
| prop.getActualRuntimeType(prop_val, marshalResult).accept(this); |
| } else { |
| rtt.accept(this); |
| } |
| setCurrObject(curr_obj, curr_prop); |
| } |
| |
| public void visit(SimpleContentRuntimeBindingType simpleContentRuntimeBindingType) |
| throws XmlException |
| { |
| firstVisit(currObject, currProp, simpleContentRuntimeBindingType); |
| //no element children so we are finished. |
| } |
| |
| public void visit(SimpleRuntimeBindingType simpleRuntimeBindingType) |
| throws XmlException |
| { |
| firstVisit(currObject, currProp, simpleRuntimeBindingType); |
| //no element children so we are finished. |
| } |
| |
| public void visit(JaxrpcEnumRuntimeBindingType jaxrpcEnumRuntimeBindingType) |
| throws XmlException |
| { |
| firstVisit(currObject, currProp, jaxrpcEnumRuntimeBindingType); |
| //no element children so we are finished. |
| } |
| |
| public void visit(WrappedArrayRuntimeBindingType wrappedArrayRuntimeBindingType) |
| throws XmlException |
| { |
| final Object curr_obj = currObject; |
| final RuntimeBindingProperty curr_prop = currProp; |
| |
| if (firstVisit(curr_obj, curr_prop, wrappedArrayRuntimeBindingType)) { |
| return; |
| } |
| |
| final RuntimeBindingProperty elem_prop = |
| wrappedArrayRuntimeBindingType.getElementProperty(); |
| |
| if (elem_prop.getRuntimeBindingType().isJavaPrimitive()) return; |
| |
| |
| //REVIEW: consider direct array access |
| final Iterator itr = ArrayUtils.getCollectionIterator(curr_obj); |
| while (itr.hasNext()) { |
| final Object item = itr.next(); |
| setCurrObject(item, elem_prop); |
| elem_prop.getActualRuntimeType(item, marshalResult).accept(this); |
| } |
| setCurrObject(curr_obj, curr_prop); |
| } |
| |
| public void visit(SoapArrayRuntimeBindingType soapArrayRuntimeBindingType) |
| throws XmlException |
| { |
| final Object curr_obj = currObject; |
| final RuntimeBindingProperty curr_prop = currProp; |
| |
| if (firstVisit(curr_obj, curr_prop, soapArrayRuntimeBindingType)) { |
| return; |
| } |
| |
| final RuntimeBindingProperty elem_prop = |
| soapArrayRuntimeBindingType.getElementProperty(); |
| |
| if (elem_prop.getRuntimeBindingType().isJavaPrimitive()) return; |
| |
| |
| throw new AssertionError("UNIMP"); |
| |
| |
| // |
| // //REVIEW: consider direct array access |
| // final Iterator itr = ArrayUtils.getCollectionIterator(curr_obj); |
| // while (itr.hasNext()) { |
| // final Object item = itr.next(); |
| // setCurrObject(item, elem_prop); |
| // elem_prop.getActualRuntimeType(item, marshalResult).accept(this); |
| // } |
| // |
| // |
| |
| |
| //setCurrObject(curr_obj, curr_prop); |
| } |
| |
| public void visit(ListArrayRuntimeBindingType listArrayRuntimeBindingType) |
| throws XmlException |
| { |
| firstVisit(currObject, currProp, listArrayRuntimeBindingType); |
| //no element children so we are finished. |
| } |
| |
| //return true if we should stop processing! |
| private boolean firstVisit(Object obj, |
| RuntimeBindingProperty property, |
| final RuntimeBindingType actual_rtt) |
| |
| { |
| final boolean needs_xsi_type = |
| (actual_rtt != property.getRuntimeBindingType()); |
| |
| //TODO: consider adding actual_rtt to ref table so we don't have |
| //to calc it again |
| |
| return objectRefTable.incrementRefCount(obj, property, needs_xsi_type) > 1; |
| } |
| |
| } |
| |
| } |