/******************************************************************************
 * Copyright (c) 2010 Oracle
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * Contributors:
 *    Konstantin Komissarchik - initial implementation and ongoing maintenance
 ******************************************************************************/

package org.eclipse.wst.common.project.facet.core.util.internal;

import java.io.IOException;
import java.io.Writer;
import java.util.LinkedList;

/**
 * Facility for writing XML files.
 * 
 * @author <a href="konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
 */

public final class XmlWriter
{
    public static final String ENCODING = "UTF-8"; //$NON-NLS-1$
    
    private static final String NL 
        = System.getProperty( "line.separator" ); //$NON-NLS-1$
    
    private final Writer base;
    private final String singleIndent;
    private final LinkedList<Context> elementStack;
    
    private static final class Context
    {
        public String element;
        public boolean hasChildren;
        
        public Context( final String element )
        {
            this.element = element;
            this.hasChildren = false;
        }
    }
    
    public XmlWriter( final Writer base )
    {
        this( base, "  " ); //$NON-NLS-1$
    }
    
    public XmlWriter( final Writer base,
                      final String indent )
    {
        this.base = base;
        this.singleIndent = indent;
        this.elementStack = new LinkedList<Context>();
    }
    
    public void startElement( final String name )
    
        throws IOException
        
    {
        if( this.elementStack.size() > 0 )
        {
            final Context parentContext = this.elementStack.getLast();
            
            if( ! parentContext.hasChildren )
            {
                this.base.write( '>' );
                this.base.write( NL );
            }
            
            parentContext.hasChildren = true;
        }
        
        indent();
        
        this.base.write( '<' );
        this.base.write( name );
        
        this.elementStack.addLast( new Context( name ) );
    }
    
    public void endElement()
    
        throws IOException
        
    {
        if( this.elementStack.isEmpty() )
        {
            throw new RuntimeException();
        }
        
        final Context context = this.elementStack.removeLast();
        
        if( context.hasChildren )
        {
            indent();
            this.base.write( "</" ); //$NON-NLS-1$
            this.base.write( context.element );
            this.base.write( ">" ); //$NON-NLS-1$
            this.base.write( NL );
        }
        else
        {
            this.base.write( "/>" ); //$NON-NLS-1$
            this.base.write( NL );
        }
    }
    
    public void addAttribute( final String name,
                              final String value )
    
        throws IOException
        
    {
        if( this.elementStack.isEmpty() )
        {
            throw new RuntimeException();
        }
        
        if( this.elementStack.getLast().hasChildren )
        {
            throw new RuntimeException();
        }
        
        this.base.write( ' ' );
        this.base.write( name );
        this.base.write( "=\"" ); //$NON-NLS-1$
        this.base.write( value );
        this.base.write( '"' );
    }
    
    public void flush()
    
        throws IOException
        
    {
        this.base.flush();
    }
    
    private void indent()
    
        throws IOException
        
    {
        for( int i = 0, n = this.elementStack.size(); i < n; i++ )
        {
            this.base.write( this.singleIndent );
        }
    }
    
}
