/*******************************************************************************
* Copyright (c) 2015 IBH SYSTEMS GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBH SYSTEMS GmbH - initial API and implementation
*******************************************************************************/
package org.eclipse.packagedrone.repo.web.sitemap;
import java.io.IOException;
import java.io.Writer;
import java.net.URI;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Optional;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
public class SitemapIndexWriter implements SitemapIndexContext, AutoCloseable
{
private static final String NS = "http://www.sitemaps.org/schemas/sitemap/0.9";
private static final String NL = "\n";
private static final String IN = " ";
private static final String IN2 = IN + IN;
private static DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern ( "yyyy-MM-dd'T'hh:mm:ssXXX", Locale.ROOT );
private final String urlPrefix;
private final XMLStreamWriter out;
private boolean finished;
private final Writer writer;
public SitemapIndexWriter ( final Writer writer, final String urlPrefix, final XMLOutputFactory outputFactory ) throws IOException
{
this.writer = writer;
this.urlPrefix = urlPrefix;
try
{
this.out = outputFactory.createXMLStreamWriter ( writer );
}
catch ( final XMLStreamException e )
{
throw new IOException ( e );
}
writeStart ();
}
private void writeEnd () throws IOException
{
try
{
this.out.writeEndElement ();
this.out.writeEndDocument ();
}
catch ( final XMLStreamException e )
{
throw new IOException ( e );
}
}
private void writeStart () throws IOException
{
try
{
this.out.writeStartDocument ( "UTF-8", "1.0" );
this.out.writeCharacters ( NL );
this.out.writeStartElement ( "sitemapindex" );
this.out.writeDefaultNamespace ( NS );
this.out.writeCharacters ( NL );
}
catch ( final XMLStreamException e )
{
throw new IOException ( e );
}
}
@Override
public void addLocation ( String localUrl, final Optional<Instant> lastModification )
{
if ( !localUrl.startsWith ( "/" ) )
{
localUrl = "/" + localUrl;
}
try
{
this.out.writeCharacters ( IN );
this.out.writeStartElement ( "sitemap" );
this.out.writeCharacters ( NL );
writeTag ( this.out, "loc", URI.create ( this.urlPrefix + localUrl ).toASCIIString () );
if ( lastModification.isPresent () )
{
writeTag ( this.out, "lastmod", FORMAT.format ( lastModification.get ().atZone ( ZoneId.systemDefault () ) ) );
}
this.out.writeCharacters ( IN );
this.out.writeEndElement ();
this.out.writeCharacters ( NL );
}
catch ( final XMLStreamException e )
{
throw new RuntimeException ( e );
}
}
private void writeTag ( final XMLStreamWriter out, final String tagName, final String value ) throws XMLStreamException
{
out.writeCharacters ( IN2 );
out.writeStartElement ( tagName );
out.writeCharacters ( value );
out.writeEndElement ();
out.writeCharacters ( NL );
}
/**
* Complete the document but don't close the underlying writer
*
* @throws IOException
* if there is an IO error
*/
public void finish () throws IOException
{
if ( !this.finished )
{
this.finished = true;
writeEnd ();
}
try
{
this.out.close ();
}
catch ( final XMLStreamException e )
{
throw new IOException ( e );
}
}
/**
* Complete the document and close the underlying writer
*
* @throws IOException
* if there is an IO error
*/
@Override
public void close () throws IOException
{
try
{
finish ();
}
finally
{
this.writer.close ();
}
}
}