/*
 * Copyright 2007 Le Duc Bao
 *
 * Licensed 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.castor.ddlgen;

import java.io.PrintWriter;

/**
 * Replace PrintStream and StringBuffer by a Writer implementation
 * We have various properties to configure output that are in-depended of the schema object:
 * <li/>org.castor.ddlgen.CharFormat=SENSITIVE, UPPER and LOWER
 * <li/>org.castor.ddlgen.Newline=\n
 * <li/>org.castor.ddlgen.Indention=\t
 * 
 * These properties are accessed at various places all around ddlgen at the moment.The idea 
 * is that these properties are set only once at the new Writer and do not need to be 
 * accessed elsewhere. This has the following advantages:
 * <li/>improved performance as the properties don't need to be accessed for every object to output
 * <li/>functionallity to format genertaed ddl is concentrated in one class: the new Writer
 * <li/>all the toDDL(), toDropDDL(), toCreateDDL() methods get much shorter
 * 
 * I thought of the following interface for the new Writer (not complete):
 * <li/>write(String) outputs String as is
 * <li/>writeln(String) calls write(String) followed by newline()
 * <li/>newline() output newline and indention of next line
 * <li/>indent() increases indention
 * <li/>unindent() decreases indention
 * 
 * More write() and writeln() methods for other data types may be added on demand. A further 
 * improvement could be to offer write(String, Object[])  methods that internally use 
 * MessageFormat. This would enable us to use a pattern based approach for DDL generation. 
 * These patterns may sometimes be much easier to read and maintain.
 * 
 * In addition to the introduction of the new Writer it will be required to pass an instance 
 * of the Writer to every method where DDL gets generated. Therefore the parameterless 
 * toCreate() method have to be changed to toCreateDDL(DDLWriter). This also applies to other 
 * such methods.

 * @author <a href="mailto:leducbao@gmail.com">Le Duc Bao</a>
 */

public class DDLWriter {
    //----------------variables---------------------------------------------
    
    /** handle configuration. */
//    private Configuration _conf = null;

    /** handle the current indent. */
    private int _indentLevel = 0;

    /** A principal print writer. */ 
    private PrintWriter _pw = null;
    
    /**new line. */
    private String _newline = null;

    /**indent. */
    private String _indent = null;
    //-----------------constructors----------------------------------------
    
    /**
     * default constructor. 
     *@param pw a Print Writer to be wrapped.
     *@param conf global Configuration.
     */
    public DDLWriter (final PrintWriter pw, final Configuration conf) {
        _pw = pw;
        initialize (conf);
    }
    //-------------------------private methodes----------------------------
    /**
     * get some default configuration.
     *@param conf global configuration.
     */
    private void initialize (final Configuration conf) {
        _newline = conf.getStringValue(
                DDLGenConfiguration.NEWLINE_KEY, DDLGenConfiguration.DEFAULT_NEWLINE);
        _indent = conf.getStringValue(
                DDLGenConfiguration.INDENT_KEY, DDLGenConfiguration.DEFAULT_INDENT);        
    }
    //-------------------------public methods------------------------------

    /**
     * create a newline in output stream.
     */    
    public final void newline() {
        _pw.print (_newline);
        for (int i = 0; i < _indentLevel; i++) {
            _pw.print (_indent);
        }
    }
    
    /**
     * add one more tab for a newline.
     */
    public final void indent() {
        _indentLevel++;
    }
    
    /**
     * remove one tab of current indented tab of a newline.
     */
    public final void unindent () {
        _indentLevel = _indentLevel == 0 ? 0 : _indentLevel--;
    }
    
    /**
     * write a string S into output stream.
     * @param s string to be written.
     * @return a this writer
     */
    public final DDLWriter write(final String s) {
        _pw.print (s);
        return this;
    }
    
    /**
     * write an integer number into output stream.
     * @param number number to be written.
     * @return a this writer
     */
    public final DDLWriter write(final int number) {
        _pw.print (number);
        return this;
    }

    /**
     * write a double number into output stream.
     * @param number number to be written.
     * @return a this writer
     */
    public final DDLWriter write(final double number) {
        _pw.print (number);
        return this;
    }
    
    /**
     * write a character into output stream.
     * @param c character to be written.
     * @return a this writer
     */
    public final DDLWriter write(final char c) {
        _pw.print (c);
        return this;
    }
    
    /**
     * write an array of character into output stream.
     * @param c an array of character to be written.
     * @return a this writer
     */
    public final DDLWriter write(final char[] c) {
        _pw.print (c);
        return this;
    }
    
    /**
     * write a character into output stream.
     * @param l a number number to be written.
     * @return a this writer
     */
    public final DDLWriter write(final long l) {
        _pw.print (l);
        return this;
    }
    
    /**
     * write an object into output stream.
     * @param o an object to be written.
     * @return a this writer
     */
    public final DDLWriter writeObject(final Object o) {
        _pw.print (o);
        return this;
    }
        
     /** A convenience method to write a formatted string to this writer using
     * the specified format string and arguments.  If automatic flushing is
     * enabled, calls to this method will flush the output buffer.
     *
     * <p> An invocation of this method of the form <tt>out.printf(format,
     * args)</tt> behaves in exactly the same way as the invocation
     *
     * <pre>
     *     out.format(format, args) </pre>
     *
     * @param  format
     *         A format string as described in <a
     *         href="../util/Formatter.html#syntax">Format string syntax</a>.
     *
     * @param  args
     *         Arguments referenced by the format specifiers in the format
     *         string.  If there are more arguments than format specifiers, the
     *         extra arguments are ignored.  The number of arguments is
     *         variable and may be zero.  The maximum number of arguments is
     *         limited by the maximum dimension of a Java array as defined by
     *         the <a href="http://java.sun.com/docs/books/vmspec/">Java
     *         Virtual Machine Specification</a>.  The behaviour on a
     *         <tt>null</tt> argument depends on the <a
     *         href="../util/Formatter.html#syntax">conversion</a>.
     *
     * @return  this writer
     */
    public final DDLWriter writeObject(final String format, final Object ... args) {
        _pw.printf (format, args);
        return this;
    }
    
    /**
     * write internally use  MessageFormat enabling to use a pattern based 
     * input string.
     * @param format a formating string.
     * @param objs an array of object to be formated
     * @return  this writer
     */
    public final DDLWriter writeObjects(final String format, final Object[] objs) {
        _pw.printf (format, objs);
        return this;
    }
    
    /**
     * write a string S into output stream.
     * @param s string to be written.
     * @return  this writer
     */
    public final DDLWriter writeln(final String s) {
        _pw.print (s);
        newline();
        return this;
    }
    
    /**
     * write an integer number into output stream.
     * @param number number to be written.
     * @return  this writer
     */
    public final DDLWriter writeln(final int number) {
        _pw.print (number);
        newline();
        return this;
    }

    /**
     * write a double number into output stream.
     * @param number number to be written.
     * @return  this writer
     */
    public final DDLWriter writeln(final double number) {
        _pw.print (number);
        newline();
        return this;
    }
    
    /**
     * write a character into output stream.
     * @param c a character to be written.
     * @return  this writer
     */
    public final DDLWriter writeln(final char c) {
        _pw.print (c);
        newline();
        return this;
    }
    
    /**
     * write an array of character into output stream.
     * @param c a character to be written.
     * @return  this writer
     */
    public final DDLWriter writeln(final char[] c) {
        _pw.print (c);
        newline();
        return this;
    }
    
    /**
     * write a character into output stream.
     * @param l a number to be written.
     * @return  this writer
     */
    public final DDLWriter writeln(final long l) {
        _pw.print (l);
        newline();
        return this;
    }
    
    /**
     * write an object into output stream.
     * @param o an object to be written.
     * @return  this writer
     */
    public final DDLWriter writelnObject(final Object o) {
        _pw.print (o);
        newline();
        return this;
    }
        
     /** A convenience method to write a formatted string to this writer using
     * the specified format string and arguments.  If automatic flushing is
     * enabled, calls to this method will flush the output buffer.
     *
     * <p> An invocation of this method of the form <tt>out.printf(format,
     * args)</tt> behaves in exactly the same way as the invocation
     *
     * <pre>
     *     out.format(format, args) </pre>
     *
     * @param  format
     *         A format string as described in <a
     *         href="../util/Formatter.html#syntax">Format string syntax</a>.
     *
     * @param  args
     *         Arguments referenced by the format specifiers in the format
     *         string.  If there are more arguments than format specifiers, the
     *         extra arguments are ignored.  The number of arguments is
     *         variable and may be zero.  The maximum number of arguments is
     *         limited by the maximum dimension of a Java array as defined by
     *         the <a href="http://java.sun.com/docs/books/vmspec/">Java
     *         Virtual Machine Specification</a>.  The behaviour on a
     *         <tt>null</tt> argument depends on the <a
     *         href="../util/Formatter.html#syntax">conversion</a>.
     * @return  This writer
     */
    public final DDLWriter writelnObject(final String format, final Object ... args) {
        _pw.printf (format, args);
        newline();
        return this;
    }
    
    /**
     * write internally use  MessageFormat enabling to use a pattern based 
     * input string.
     * @param format a formating string.
     * @param objs an array of object to be formated
     * @return  this writer
     */
    public final DDLWriter writelnObjects(final String format, final Object[] objs) {
        _pw.printf (format, objs);
        newline();
        return this;
    }
 }

