content: /*
   * 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.camel.component.xslt;
  
  import java.io.InputStream;
  
  import javax.xml.parsers.ParserConfigurationException;
  import javax.xml.transform.Source;
  import javax.xml.transform.TransformerException;
  import javax.xml.transform.dom.DOMSource;
  import javax.xml.transform.sax.SAXSource;
  import javax.xml.transform.stream.StreamSource;
  
  import org.w3c.dom.Node;
  
  import org.apache.camel.Exchange;
  import org.apache.camel.ExpectedBodyTypeException;
  import org.apache.camel.Expression;
  import org.apache.camel.RuntimeTransformException;
  import org.apache.camel.TypeConverter;
  import org.apache.camel.support.builder.xml.XMLConverterHelper;
  
  /**
   * Handler for xml sources
   */
  public class XmlSourceHandlerFactoryImpl implements SourceHandlerFactory {
  
      private XMLConverterHelper converter = new XMLConverterHelper();
      private boolean isFailOnNullBody = true;
  
      /**
       * Returns true if we fail when the body is null.
       */
      public boolean isFailOnNullBody() {
          return isFailOnNullBody;
      }
  
      /**
       * Set if we should fail when the body is null
       */
      public void setFailOnNullBody(boolean failOnNullBody) {
          isFailOnNullBody = failOnNullBody;
      }
  
      @Override
      public Source getSource(Exchange exchange, Expression source) throws Exception {
          // only convert to input stream if really needed
          if (isInputStreamNeeded(exchange)) {
              InputStream is = exchange.getIn().getBody(InputStream.class);
              return getSource(exchange, is);
          } else {
              Object body = source != null ? source.evaluate(exchange, Object.class) : exchange.getMessage().getBody();
              return getSource(exchange, body);
          }
      }
  
      /**
       * Checks whether we need an {@link InputStream} to access the message body.
       * <p/>
       * Depending on the content in the message body, we may not need to convert to {@link InputStream}.
       *
       * @param  exchange the current exchange
       * @return          <tt>true</tt> to convert to {@link InputStream} beforehand converting to {@link Source}
       *                  afterwards.
       */
      protected boolean isInputStreamNeeded(Exchange exchange) {
          Object body = exchange.getIn().getBody();
          if (body == null) {
              return false;
          }
  
          if (body instanceof InputStream) {
              return true;
          } else if (body instanceof Source) {
              return false;
          } else if (body instanceof String) {
              return false;
          } else if (body instanceof byte[]) {
              return false;
          } else if (body instanceof Node) {
              return false;
          } else if (exchange.getContext().getTypeConverterRegistry().lookup(Source.class, body.getClass()) != null) {
              //there is a direct and hopefully optimized converter to Source
              return false;
          }
          // yes an input stream is needed
          return true;
      }
  
      /**
       * Converts the inbound body to a {@link Source}, if the body is <b>not</b> already a {@link Source}.
       * <p/>
       * This implementation will prefer to source in the following order:
       * <ul>
       * <li>StAX - If StAX is allowed</li>
       * <li>SAX - SAX as 2nd choice</li>
       * <li>Stream - Stream as 3rd choice</li>
       * <li>DOM - DOM as 4th choice</li>
       * </ul>
       */
      protected Source getSource(Exchange exchange, Object body) {
          // body may already be a source
          if (body instanceof Source) {
              return (Source) body;
          }
          Source source = null;
          if (body != null) {
              // then try SAX
              source = exchange.getContext().getTypeConverter().tryConvertTo(SAXSource.class, exchange, body);
              if (source == null) {
                  // then try stream
                  source = exchange.getContext().getTypeConverter().tryConvertTo(StreamSource.class, exchange, body);
              }
              if (source == null) {
                  // and fallback to DOM
                  source = exchange.getContext().getTypeConverter().tryConvertTo(DOMSource.class, exchange, body);
              }
              // as the TypeConverterRegistry will look up source the converter differently if the type converter is loaded different
              // now we just put the call of source converter at last
              if (source == null) {
                  TypeConverter tc = exchange.getContext().getTypeConverterRegistry().lookup(Source.class, body.getClass());
                  if (tc != null) {
                      source = tc.convertTo(Source.class, exchange, body);
                  }
              }
          }
  
          if (source == null) {
              if (isFailOnNullBody()) {
                  throw new ExpectedBodyTypeException(exchange, Source.class);
              } else {
                  try {
                      source = converter.toDOMSource(converter.createDocument());
                  } catch (ParserConfigurationException | TransformerException e) {
                      throw new RuntimeTransformException(e);
                  }
              }
          }
  
          return source;
      }
  }
  