1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.xml.internal.bind.v2.runtime.unmarshaller;
  27 
  28 import javax.xml.bind.annotation.DomHandler;
  29 import javax.xml.transform.Result;
  30 import javax.xml.transform.sax.TransformerHandler;
  31 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
  32 import org.xml.sax.SAXException;
  33 
  34 /**
  35  * Loads a DOM.
  36  *
  37  * @author Kohsuke Kawaguchi
  38  */
  39 public class DomLoader<ResultT extends Result> extends Loader {
  40 
  41     private final DomHandler<?,ResultT> dom;
  42 
  43     /**
  44      * Used to capture the state.
  45      *
  46      * This instance is created for each unmarshalling episode.
  47      */
  48     private final class State {
  49 
  50         /** This handler will receive SAX events. */
  51         private TransformerHandler handler = null;
  52 
  53         /** {@link #handler} will produce this result. */
  54         private final ResultT result;
  55 
  56         // nest level of elements.
  57         int depth = 1;
  58 
  59         public State( UnmarshallingContext context ) throws SAXException {
  60             handler = JAXBContextImpl.createTransformerHandler(context.getJAXBContext().disableSecurityProcessing);
  61             result = dom.createUnmarshaller(context);
  62 
  63             handler.setResult(result);
  64 
  65             // emulate the start of documents
  66             try {
  67                 handler.setDocumentLocator(context.getLocator());
  68                 handler.startDocument();
  69                 declarePrefixes( context, context.getAllDeclaredPrefixes() );
  70             } catch( SAXException e ) {
  71                 context.handleError(e);
  72                 throw e;
  73             }
  74         }
  75 
  76         public Object getElement() {
  77             return dom.getElement(result);
  78         }
  79 
  80         private void declarePrefixes( UnmarshallingContext context, String[] prefixes ) throws SAXException {
  81             for( int i=prefixes.length-1; i>=0; i-- ) {
  82                 String nsUri = context.getNamespaceURI(prefixes[i]);
  83                 if(nsUri==null)     throw new IllegalStateException("prefix \'"+prefixes[i]+"\' isn't bound");
  84                 handler.startPrefixMapping(prefixes[i],nsUri );
  85             }
  86         }
  87 
  88         private void undeclarePrefixes( String[] prefixes ) throws SAXException {
  89             for( int i=prefixes.length-1; i>=0; i-- )
  90                 handler.endPrefixMapping( prefixes[i] );
  91         }
  92     }
  93 
  94     public DomLoader(DomHandler<?, ResultT> dom) {
  95         super(true);
  96         this.dom = dom;
  97     }
  98 
  99     @Override
 100     public void startElement(UnmarshallingContext.State state, TagName ea) throws SAXException {
 101         UnmarshallingContext context = state.getContext();
 102         if (state.getTarget() == null)
 103             state.setTarget(new State(context));
 104 
 105         State s = (State) state.getTarget();
 106         try {
 107             s.declarePrefixes(context, context.getNewlyDeclaredPrefixes());
 108             s.handler.startElement(ea.uri, ea.local, ea.getQname(), ea.atts);
 109         } catch (SAXException e) {
 110             context.handleError(e);
 111             throw e;
 112         }
 113     }
 114 
 115     @Override
 116     public void childElement(UnmarshallingContext.State state, TagName ea) throws SAXException {
 117         state.setLoader(this);
 118         State s = (State) state.getPrev().getTarget();
 119         s.depth++;
 120         state.setTarget(s);
 121     }
 122 
 123     @Override
 124     public void text(UnmarshallingContext.State state, CharSequence text) throws SAXException {
 125         if(text.length()==0)
 126             return;     // there's no point in creating an empty Text node in DOM.
 127         try {
 128             State s = (State) state.getTarget();
 129             s.handler.characters(text.toString().toCharArray(),0,text.length());
 130         } catch( SAXException e ) {
 131             state.getContext().handleError(e);
 132             throw e;
 133         }
 134     }
 135 
 136     @Override
 137     public void leaveElement(UnmarshallingContext.State state, TagName ea) throws SAXException {
 138         State s = (State) state.getTarget();
 139         UnmarshallingContext context = state.getContext();
 140 
 141         try {
 142             s.handler.endElement(ea.uri, ea.local, ea.getQname());
 143             s.undeclarePrefixes(context.getNewlyDeclaredPrefixes());
 144         } catch( SAXException e ) {
 145             context.handleError(e);
 146             throw e;
 147         }
 148 
 149         if((--s.depth)==0) {
 150             // emulate the end of the document
 151             try {
 152                 s.undeclarePrefixes(context.getAllDeclaredPrefixes());
 153                 s.handler.endDocument();
 154             } catch( SAXException e ) {
 155                 context.handleError(e);
 156                 throw e;
 157             }
 158 
 159             // we are done
 160             state.setTarget(s.getElement());
 161         }
 162     }
 163 
 164 }