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

thredds.catalog2.xml.parser.stax.StaxThreddsXmlParserUtils Maven / Gradle / Ivy

Go to download

The NetCDF-Java Library is a Java interface to NetCDF files, as well as to many other types of scientific data formats.

The newest version!
/*
 * Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata
 *
 * Portions of this software were developed by the Unidata Program at the
 * University Corporation for Atmospheric Research.
 *
 * Access and use of this software shall impose the following obligations
 * and understandings on the user. The user is granted the right, without
 * any fee or cost, to use, copy, modify, alter, enhance and distribute
 * this software, and any derivative works thereof, and its supporting
 * documentation for any purpose whatsoever, provided that this entire
 * notice appears in all copies of the software, derivative works and
 * supporting documentation.  Further, UCAR requests that the user credit
 * UCAR/Unidata in any publications that result from the use of this
 * software or in any product that includes this software. The names UCAR
 * and/or Unidata, however, may not be used in any advertising or publicity
 * to endorse or promote any products or commercial entity unless specific
 * written permission is obtained from UCAR/Unidata. The user also
 * understands that UCAR/Unidata is not obligated to provide the user with
 * any support, consulting, training or assistance of any kind with regard
 * to the use, operation and performance of this software nor to provide
 * the user with any updates, revisions, new versions or "bug fixes."
 *
 * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
 */
package thredds.catalog2.xml.parser.stax;

import thredds.catalog2.xml.parser.ThreddsXmlParserException;
import thredds.catalog2.xml.parser.ThreddsXmlParserIssue;
import thredds.util.HttpUriResolver;
import thredds.util.HttpUriResolverFactory;

import javax.xml.stream.*;
import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.events.StartElement;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.util.List;
import java.util.ArrayList;
import java.net.URI;

/**
 * Utility methods for using StAX parser for THREDDS catalogs.
 *
 * @author edavis
 * @since 4.0
 */
class StaxThreddsXmlParserUtils
{
  private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger( StaxThreddsXmlParserUtils.class );

  private StaxThreddsXmlParserUtils() {}

  static boolean isEventStartOrEndElementWithMatchingName( XMLEvent event, QName elementName )
  {
    if ( event == null )
      throw new IllegalArgumentException( "Event may not be null.");

    QName eventElementName = null;
    if ( event.isStartElement() )
      eventElementName = event.asStartElement().getName();
    else if ( event.isEndElement() )
      eventElementName = event.asEndElement().getName();
    else
      return false;

    if ( eventElementName.equals( elementName ) )
      return true;
    return false;
  }

  public static StartElement readNextEventCheckItIsStartElementWithExpectedName( XMLEventReader xmlEventReader,
                                                                          QName startElementName )
          throws ThreddsXmlParserException
  {
    if ( ! xmlEventReader.hasNext() )
      throw new IllegalStateException( "XMLEventReader has no further events." );

    StartElement startElement = null;
    try
    {
      XMLEvent event = xmlEventReader.peek();
      if ( ! event.isStartElement() )
        throw new IllegalStateException( "Next event must be StartElement." );

      if ( ! event.asStartElement().getName().equals( startElementName ) )
        throw new IllegalStateException( "Start element must be an '" + startElementName.getLocalPart() + "' element." );

      startElement = xmlEventReader.nextEvent().asStartElement();
    }
    catch ( XMLStreamException e )
    {
      String msg = "Problem reading XML stream.";
      log.warn( "readNextEventCheckItIsStartElementWithExpectedName(): " + msg, e );
      throw new ThreddsXmlParserException( new ThreddsXmlParserIssue( ThreddsXmlParserIssue.Severity.FATAL, msg, null, e ) );
    }

    return startElement;
  }

  public static void readNextEventCheckItIsEndElementWithExpectedName( XMLEventReader xmlEventReader,
                                                                      QName elementName )
          throws ThreddsXmlParserException
  {
    if ( ! xmlEventReader.hasNext() )
      throw new IllegalStateException( "XMLEventReader has no further events." );

    try
    {
      XMLEvent event = xmlEventReader.peek();
      if ( ! event.isEndElement() )
        throw new IllegalStateException( "Next event must be EndElement." );

      if ( ! event.asEndElement().getName().equals( elementName ) )
        throw new IllegalStateException( "End element must be an '" + elementName.getLocalPart() + "' element." );

      xmlEventReader.nextEvent(); // .asEndElement();
    }
    catch ( XMLStreamException e )
    {
      String msg = "Problem reading XML stream.";
      log.warn( "readNextEventCheckItIsEndElementWithExpectedName(): " + msg, e );
      throw new ThreddsXmlParserException( new ThreddsXmlParserIssue( ThreddsXmlParserIssue.Severity.FATAL, msg, null, e ) );
    }
  }

  static String getLocationInfo( XMLEventReader xmlEventReader )
  {
    Location location = getLocation( xmlEventReader);
    StringBuilder sb = new StringBuilder()
            .append( "Location: SysId[")
            .append( location.getSystemId())
            .append( "] line[")
            .append( location.getLineNumber())
            .append( "] column[" )
            .append( location.getColumnNumber())
            .append( "] charOffset[" )
            .append( location.getCharacterOffset())
            .append( "]." );
    return sb.toString();
  }

  static Location getLocation( XMLEventReader xmlEventReader)
  {
    if ( xmlEventReader == null )
      throw new IllegalArgumentException( "XMLEventReader may not be null.");
    if ( ! xmlEventReader.hasNext() )
      throw new IllegalArgumentException( "XMLEventReader must have next event.");

    XMLEvent nextEvent = null;
    try
    {
      nextEvent = xmlEventReader.peek();
    }
    catch ( XMLStreamException e )
    {
      throw new IllegalArgumentException( "Could not peek() next event.");
    }

    return nextEvent.getLocation();
  }

  static ThreddsXmlParserIssue createIssueForException( String message, XMLEventReader xmlEventReader, Exception e )
          throws ThreddsXmlParserException
  {
    String locationInfo = getLocationInfo( xmlEventReader);
    String msg = message + ":\n    " + locationInfo + ": " + e.getMessage();
    log.debug( "createIssueForException(): " + msg );
    return new ThreddsXmlParserIssue( ThreddsXmlParserIssue.Severity.WARNING, msg, null, e );
  }

  static ThreddsXmlParserIssue createIssueForUnexpectedElement( String message, XMLEventReader xmlEventReader )
          throws ThreddsXmlParserException
  {
    String locationInfo = getLocationInfo( xmlEventReader);
    String unexpectedElemAsString = StaxThreddsXmlParserUtils.consumeElementAndConvertToXmlString( xmlEventReader );
    String msg = message + ":\n    " + locationInfo + ":\n" + unexpectedElemAsString;
    log.debug( "createIssueForUnexpectedElement(): " + msg );
    return new ThreddsXmlParserIssue( ThreddsXmlParserIssue.Severity.WARNING, msg, null, null );
  }

  static ThreddsXmlParserIssue createIssueForUnexpectedEvent( String message,
                                                                     ThreddsXmlParserIssue.Severity severity,
                                                                     XMLEventReader xmlEventReader, XMLEvent event )
          throws ThreddsXmlParserException
  {
    String locationInfo = getLocationInfo( xmlEventReader);
    String msg = message + " [" + severity.toString() + "]:\n    " + locationInfo + ":\n";
    log.debug( "createIssueForUnexpectedElement(): " + msg );
    return new ThreddsXmlParserIssue( severity, msg, null, null );
  }

  /**
   * Parse an element and all its contents and write it as XML to a String. The XMLEventReader's
   * current event must be the StartElement event for the desired element; once completed the
   * current event will be the event following that elements EndElement event.
   *
   * @param xmlEventReader the XMLEventReader from which to read the element.
   * @return the XML element and content written as a string
   * @throws ThreddsXmlParserException if any problems with the XMLEventReader or the XML element
   */
  static String consumeElementAndConvertToXmlString( XMLEventReader xmlEventReader )
          throws ThreddsXmlParserException
  {
    if ( xmlEventReader == null )
      throw new IllegalArgumentException( "XMLEventReader may not be null." );

    StringWriter writer = new StringWriter();
    String resultString;
    Location startLocation = null;
    try
    {
      XMLEvent event = xmlEventReader.peek();
      if ( ! event.isStartElement() )
        throw new IllegalArgumentException( "Next event in reader must be start element." );
      startLocation = event.getLocation();

      XMLOutputFactory oFactory = XMLOutputFactory.newFactory();
      oFactory.setProperty( XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE);
      if ( oFactory.isPropertySupported( "javax.xml.stream.isPrefixDefaulting" ))
        oFactory.setProperty( "javax.xml.stream.isPrefixDefaulting", Boolean.TRUE );
      XMLEventWriter eventWriter = oFactory.createXMLEventWriter( writer );

      // Track start and end elements so know when done.
      // Use name list as FILO, push on name of start element and pop off matching name of end element.
      List nameList = new ArrayList();
      while ( xmlEventReader.hasNext() )
      {
        event = xmlEventReader.nextEvent();
        if ( event.isStartElement() )
        {
          nameList.add( event.asStartElement().getName() );
        }
        else if ( event.isEndElement() )
        {
          QName endElemName = event.asEndElement().getName();
          QName lastName = nameList.get( nameList.size() - 1 );
          if ( lastName.equals( endElemName ) )
            nameList.remove( nameList.size() - 1 );
          else
          {
            // Parser should have had FATAL error for this.
            String msg = "Badly formed XML? End element [" + endElemName.getLocalPart() + "] doesn't match expected start element [" + lastName.getLocalPart() + "].";
            log.error( "consumeElementAndConvertToXmlString(): " + msg );
            throw new ThreddsXmlParserException( "FATAL? " + msg );
          }
        }

        eventWriter.add( event );
        if ( nameList.isEmpty() )
          break;
      }
      eventWriter.flush();
      eventWriter.close();
      resultString = writer.toString();
    }
    catch ( XMLStreamException e ) {
      throw new ThreddsXmlParserException( "Problem reading unknown element [" + startLocation + "]. Underlying cause: " + e.getMessage(), e );
    }
    return resultString;
  }

  /**
   * Return the character contents of the containing element as a String trimmed of whitespace.
   * 

* When called, the StartElement must have already been read from the reader. Until the * EndElement is reached, only Characters events are expected. Upon completion, the reader is * left with the EndElement as the next event. * * @param xmlEventReader the event reader from which to consume Characters events. * @param containingElementName the QName of the containing element. * @return a String representation of the character contents of the containing element. * @throws ThreddsXmlParserException if had trouble reading from the XMLEventReader. */ static String getCharacterContent( XMLEventReader xmlEventReader, QName containingElementName ) throws ThreddsXmlParserException { if ( xmlEventReader == null ) throw new IllegalArgumentException( "XMLEventReader may not be null." ); if ( containingElementName == null ) throw new IllegalArgumentException( "Containing element name may not be null." ); if ( ! xmlEventReader.hasNext()) throw new IllegalStateException( "XMLEventReader must have next."); StringBuilder stringBuilder = new StringBuilder(); Location location = null; try { while ( xmlEventReader.hasNext() ) { XMLEvent event = xmlEventReader.peek(); location = event.getLocation(); if ( event.isCharacters()) { event = xmlEventReader.nextEvent(); stringBuilder.append( event.asCharacters().getData()); } else if ( event.isEndElement()) { if ( event.asEndElement().getName().equals( containingElementName )) { return stringBuilder.toString().trim(); } throw new IllegalStateException( "Badly formed XML? Unexpected end element [" + event.asEndElement().getName().getLocalPart() + "]["+location+"] doesn't match expected start element [" + containingElementName.getLocalPart() + "]."); } else if ( event.isStartElement() ) { throw new IllegalStateException( "Badly formed XML? Unexpected start element [" + event.asStartElement().getName().getLocalPart() + "][" + location + "] when characters expected." ); } else { xmlEventReader.next(); } } } catch ( XMLStreamException e ) { throw new ThreddsXmlParserException( "Problem reading unknown event [" + location + "]. Underlying cause: " + e.getMessage(), e ); } throw new ThreddsXmlParserException( "Unexpected end of XMLEventReader."); } /** * Return a StreamSource given a URI. * * @param documentUri the target URI. * @return a StreamSource for the resource located at the given URI. * @throws ThreddsXmlParserException if there is a problem reading from the URI. */ static Source getSourceFromUri( URI documentUri ) throws ThreddsXmlParserException { HttpUriResolver httpUriResolver = HttpUriResolverFactory.getDefaultHttpUriResolver( documentUri ); InputStream is = null; try { httpUriResolver.makeRequest(); is = httpUriResolver.getResponseBodyAsInputStream(); } catch ( IOException e ) { throw new ThreddsXmlParserException( "Problem accessing resource [" + documentUri.toString() + "].", e ); } return new StreamSource( is, documentUri.toString() ); } /** * Return a StreamSource given a File. * * @param file the target File. * @param docBaseUri the document base URI to use for this resource or null to use the File as docBase. * @return a StreamSource for the resource located at the given File. * @throws ThreddsXmlParserException if there is a problem reading from the File. */ static Source getSourceFromFile( File file, URI docBaseUri ) throws ThreddsXmlParserException { if ( file == null ) throw new IllegalArgumentException( "File may not be null." ); Source source = null; if ( docBaseUri == null ) source = new StreamSource( file ); else { InputStream is = null; try { is = new FileInputStream( file ); } catch ( FileNotFoundException e ) { String message = "Couldn't find file [" + file.getPath() + "]."; log.error( "parseIntoBuilder(): " + message, e ); throw new ThreddsXmlParserException( message, e ); } source = new StreamSource( is, docBaseUri.toString() ); } return source; } /** * Return an XMLEventReader given a Source and an XMLInputFactory. * * @param source the source. * @param factory the factory to be used. * @return an XMLEventReader for the given Source. * @throws ThreddsXmlParserException if have problems reading the source. */ static XMLEventReader getEventReaderFromSource( Source source, XMLInputFactory factory ) throws ThreddsXmlParserException { XMLEventReader reader; try { reader = factory.createXMLEventReader( source ); } catch( XMLStreamException e ) { String message = "Problems reading stream [" + source.getSystemId() + "]."; log.error( "getEventReaderFromSource(): " + message, e ); throw new ThreddsXmlParserException( message, e ); } return reader; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy