All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.xml.bind.marshaller.SAXMarshaller Maven / Gradle / Ivy

There is a newer version: 4.0.5
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://oss.oracle.com/licenses/CDDL+GPL-1.1
 * or LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.xml.bind.marshaller;

import javax.xml.bind.JAXBException;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.helpers.ValidationEventImpl;

import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import com.sun.xml.bind.serializer.AbortSerializationException;
import com.sun.xml.bind.serializer.NamespaceContext2;
import com.sun.xml.bind.serializer.NamespaceContextImpl;
import com.sun.xml.bind.serializer.PrefixCallback;
import com.sun.xml.bind.serializer.XMLSerializable;
import com.sun.xml.bind.serializer.XMLSerializer;

/**
 * XMLSerializer that produces SAX2 events.
 * 
 * To marshal an object, create an instance of SAXMarshaller
 * and call the serializeElements method of the XMLSerializable
 * object that you want to marshal.
 * 
 * @author  Kohsuke Kawaguchi
 * @since JAXB1.0
 * @deprecated in JAXB1.0.1
 */
public class SAXMarshaller implements XMLSerializer
{
    /**
     * "Attributes" object that is passed to the startElement event.
     * One object is reused throughout the marshalling.
     */
    private final AttributesImpl attributes = new AttributesImpl();
 
    /** This object receives SAX2 events. */
    private final ContentHandler writer;
    
    /** Marshaller object to which this object belongs. */
    private final MarshallerImpl owner;
    
    /**
     * Creates a marshalling context by designating the ContentHandler
     * that receives generated SAX2 events.
     */
    public SAXMarshaller( ContentHandler _writer, MarshallerImpl _owner ) {
        this.writer = _writer;
        this.owner = _owner;
    }
    
    /** namespace context. */
    private final NamespaceContextImpl nsContext = new NamespaceContextImpl();
    
    public NamespaceContext2 getNamespaceContext() { return nsContext; }
    
    //
    //
    // name stack
    //
    //
    
    /** Element name stack implemented as an array of (uri,local) pairs. */
    private String[] elementStack = new String[16];;
    private int elementLen=0;
    
    
    
    private void pushElement( String uri, String local ) {
        if(elementStack.length==elementLen) {
            // reallocate buffer
            String[] buf = new String[elementStack.length*2];
            System.arraycopy( elementStack, 0, buf, 0, elementStack.length );
            elementStack = buf;
        }
        elementStack[elementLen++] = uri;
        elementStack[elementLen++] = local;
    }
    
    private void popElement() { elementLen-=2;  }
    
    private String getCurrentElementUri()   { return elementStack[elementLen-2]; }
    private String getCurrentElementLocal() { return elementStack[elementLen-1]; }
    
    
    
    
    
    /**
     * Starts marshalling of an element.
     * Calling this method will push the internal state into the
     * internal stack.
     */
    public void startElement( String uri, String local ) throws SAXException {
        writePendingText();
        nsContext.startElement();
        pushElement(uri,local); // memorize element name
    }
    
    
    private final PrefixCallback startPrefixCallback = new PrefixCallback() {
        public void onPrefixMapping( String prefix, String nsUri ) throws SAXException {
            writer.startPrefixMapping(prefix,nsUri);
        }
    };
    private final PrefixCallback endPrefixCallback = new PrefixCallback() {
        public void onPrefixMapping( String prefix, String nsUri ) throws SAXException {
            writer.endPrefixMapping(prefix);
        }
    };
    
    
    /**
     * Switches to the "marshal child texts/elements" mode.
     * This method has to be called after the 1st pass is completed.
     */
    public void endAttributes() throws SAXException {
        // calculate QName of the element
        String uri = getCurrentElementUri();
        String local = getCurrentElementLocal();
        
        String prefix = nsContext.declareNamespace(uri,false);
        String qname = local;
        if(prefix!=null)    qname = prefix+':'+qname;

        // fire startPrefixMapping events
        nsContext.iterateDeclaredPrefixes(startPrefixCallback);
        
        // fire the startElement event
        writer.startElement( uri, local, qname, attributes );
        
        
        // reset attributes
        attributes.clear();
        
        // prepare to collect texts
        _assert(textBuf==null);
        textBuf = new StringBuffer();
    }
    
    /**
     * Ends marshalling of an element.
     * Pops the internal stack.
     */
    public void endElement() throws SAXException {
        writePendingText();
        
        String uri = getCurrentElementUri();
        String local = getCurrentElementLocal();
        
        String prefix = nsContext.getPrefix(uri);
        String qname = local;
        if(prefix!=null)    qname = prefix+':'+qname;
        
        writer.endElement( uri, local, qname );

        // pop namespace bindings and
        // fire endPrefixMapping events
        nsContext.iterateDeclaredPrefixes(endPrefixCallback);
        
        popElement();
        
        // prepare to collect texts
        _assert(textBuf==null);
        textBuf = new StringBuffer();
        
        nsContext.endElement();
    }
    
    
    /** Buffer for collecting characters. */
    private StringBuffer textBuf = new StringBuffer();
    
    /**
     * Marshalls text.
     * 
     * 

* This method can be called (i) after the startAttribute method * and (ii) before the endAttribute method, to marshal attribute values. * If the method is called more than once, those texts are considered * as separated by whitespaces. For example, * *

     * c.startAttribute();
     * c.text("abc");
     * c.text("def");
     * c.endAttribute("","foo");
     * 
* * will generate foo="abc def". * *

* Similarly, this method can be called after the endAttributes * method to marshal texts inside elements. The same rule about * multiple invokations apply to this case, too. For example, * *

     * c.startElement("","foo");
     * c.endAttributes();
     * c.text("abc");
     * c.text("def");
     *   c.startElement("","bar");
     *   c.endAttributes();
     *   c.endElement();
     * c.text("ghi");
     * c.endElement();
     * 
* * will generate {@code abc defghi}. */ public void text( String text ) throws SAXException { // If the assertion fails, it must be a bug of xjc. // right now, we are not expecting the text method to be called. _assert(textBuf!=null); if(text==null) { ValidationEvent ev = new ValidationEventImpl( ValidationEvent.ERROR, Messages.format(Messages.ERR_MISSING_OBJECT), null, new NullPointerException() ); reportError(ev); return; } if(textBuf.length()!=0) textBuf.append(' '); textBuf.append(text); } /** * Writes pending text (characters inside elements) to the writer. * This method is called from startElement and endElement. */ private void writePendingText() throws SAXException { // assert(textBuf!=null); if(textBuf.length()!=0) writer.characters( textBuf.toString().toCharArray(), 0, textBuf.length() ); textBuf = null; } /** * Starts marshalling of an attribute. * * The marshalling of an attribute will be done by *
    *
  1. call the startAttribute method *
  2. call the text method (several times if necessary) *
  3. call the endAttribute method *
* * No two attributes can be marshalled at the same time. * Note that the whole attribute marshalling must be happened * after the startElement method and before the endAttributes method. */ public void startAttribute( String uri, String local ) { _assert(textBuf==null); // allocate a buffer to collect attribute value textBuf = new StringBuffer(); // remember the attribute name. We'll use this value later. this.attNamespaceUri = uri; this.attLocalName = local; } // used to keep attribute names until the endAttribute method is called. private String attNamespaceUri; private String attLocalName; public void endAttribute() { // use CDATA as the attribute type. This preserves // successive processors to collapse whitespaces. // (we cannot prevent characters like #xD to be replaced to // #x20, though). // // strictly speaking, attribute value normalization should be // provessed by XML parser, so it's unclear whether XML writer // uses this type value. // // in any way, CDATA type is the safest choice here. String qname; if(attNamespaceUri.length()==0) { // default namespace. don't need prefix qname = attLocalName; } else { qname = nsContext.declareNamespace(attNamespaceUri,true)+':'+attLocalName; } attributes.addAttribute(attNamespaceUri,attLocalName,qname,"CDATA",textBuf.toString()); // free text buffer textBuf = null; } // we are not interested in checking the consistency of ID/IDREF. public String onID ( String value ) throws SAXException { return value; } public String onIDREF( String value ) throws SAXException { return value; } public void childAsElements( XMLSerializable o ) throws SAXException { if(o==null) { // if null is passed, it usually means that the content tree object // doesn't have some of its required property. missingObjectError(); // as a marshaller, we should be generous, so we'll continue to marshal // this document by skipping this missing object. return; } o.serializeElements(this); } public void childAsAttributes( XMLSerializable o ) throws SAXException { if(o==null) { missingObjectError(); return; } o.serializeAttributes(this); } public void childAsAttributeBodies( XMLSerializable o ) throws SAXException { if(o==null) { missingObjectError(); return; } o.serializeAttributeBodies(this); } public void reportError( ValidationEvent ve ) throws AbortSerializationException { ValidationEventHandler handler; try { handler = owner.getEventHandler(); } catch( JAXBException e ) { throw new AbortSerializationException(e); } if(!handler.handleEvent(ve)) throw new AbortSerializationException(ve.getMessage()); } // TODO: probably we should have WrappedSAXException here, // so that we can throw MarshalException directly from this method. // // TODO: unless we change the method signature of childAsXXXX and take the parent object // as one of the parameters, we can't report any location associated with it. private void missingObjectError() throws SAXException { ValidationEvent ev = new ValidationEventImpl( ValidationEvent.ERROR, Messages.format(Messages.ERR_MISSING_OBJECT), null ); reportError(ev); } private static void _assert( boolean b ) { if(!b) throw new InternalError( Messages.format( Messages.ASSERT_FAILED ) ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy