/*
 * (C) Copyright IBM Corp. 1997-1998  All rights reserved.
 *
 * US Government Users Restricted Rights Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 * The program is provided "as is" without any warranty express or
 * implied, including the warranty of non-infringement and the implied
 * warranties of merchantibility and fitness for a particular purpose.
 * IBM will not be liable for any damages suffered by you as a result
 * of using the Program. In no event will IBM be liable for any
 * special, indirect or consequential damages or lost profits even if
 * IBM has been advised of the possibility of their occurrence. IBM
 * will not be liable for any third party claims against you.
 */

package com.ibm.xml.parser;

import java.io.*;
import java.net.*;
import java.util.Stack;

/**
 * Stderr provides the XML4J parser's default implementations of the ErrorListener and
 * StreamProducer interfaces.
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 * @see com.ibm.xml.parser.ErrorListener
 * @see com.ibm.xml.parser.StreamProducer
 */
public class Stderr implements ErrorListener, StreamProducer {
    
    public  static  PrintWriter printer =   new PrintWriter(System.err);
    private         String      name    =   null;
    private         URL         url     =   null;
    private         Stack       stack   =   new Stack();

    /**
     * Constructor.
     * @param   name    URL or filespec to use as the default input stream; if a filespec is
     *                  provided, this value can include a drive and directory spec.
     *                  This value is also used to associate a name with errors reported to the
     *                  default error listener (i.e. <code>error()</code> method) that 
     *                  have a <var>file</var> parameter <code>=null</code>.
     * @see #error
     */
    public Stderr(String name) {
        this.name = name;
        try {
            this.url = new URL(this.name);
        } catch (MalformedURLException e) {
            try {
                this.url = file2URL(name);
            } catch (MalformedURLException ex) {
                ex.printStackTrace();
            } catch (SecurityException ex2) {
                printer.println("Specify a complete URL");
            }
        }
    }
    
    /**
     * Returns a file-protocol URL constructed from a context of the specified 
     * <var>file</var> and the JVM's system property &quot;user.dir&quot;.
     * @param   file    The file to use in constructing the URL; this value can include a 
     *                  drive and directory spec.
     * @exception  MalformedURLException    Thrown if unable to construct a URL based on
     *                                      the specified <var>file</var>.
     */
    public static URL file2URL(String file) throws MalformedURLException {
        String basedir = System.getProperty("user.dir").replace(File.separatorChar, '/')+"/";
        if (basedir.charAt(0) != '/')  basedir = "/"+basedir;
        URL u = new URL("file", "", basedir);
        if (file.length() >= 2 && file.charAt(1) == ':') {
            char drive = Character.toUpperCase(file.charAt(0));
            if ('A' <= drive && drive <= 'Z')
                file = "file:///"+file;
        }
        return new URL(u, file);
    }

    /**
     * <p>Listen for XML4J parser errors, and reports these errors through the standard
     * error stream (System.err).
     * <p>This method is defined by ErrorListener.
     * @param   file        Processing file, or <var>null</var> if this constructor's 
     *                      <var>name</var> parameter is to be used.
     * @param   lineNo      The line number where the current document event ends, or 
     *                      <var>-1</var> if not available.  Note that this is the line 
     *                      position of the first character after the text associated with 
     *                      the document event.  Do NOT trust this value when the input 
     *                      stream includes invalid octet as its encoding.
     * @param   charOffset  The column number where the current document event ends, or
     *                      <var>-1</var> if not available.  Note that this is the column 
     *                      number of the first character after the text associated with the 
     *                      document event.  The first column in a line is position 1.  Do NOT
     *                      trust this value when the input stream includes invalid octet as its encoding.
     * @param   key         The object may be an instance of <code>String</code> or <code>Exception</code>.
     *                      When this object is <code>String</code>, this value may help 
     *                      classify <var>msg</var> as an error (begins with "E_") or warning
     *                      (begins with "W_").
     * @param   msg         The error or warning message.
     * @see com.ibm.xml.parser.ErrorListener
     */
    public void error(String file, int lineNo, int charOffset, Object key, String msg) {
        if (null == file)  file = this.name;
        if (0 < charOffset)  charOffset --;
        printer.println(file+": "+lineNo+", "+charOffset+": "+msg);
        printer.flush();
    }

   /**
     * <p>Returns the source of the input stream (could be a character stream or a byte stream) 
     * based on this constructor's <var>name</var> parameter and the URL specification 
     * given by <var>systemID</var>.
     * <p>This method is defined by StreamProducer.
     * @param   name        CURRENTLY NOT IMPLEMENTED. 
     * @param   publicID    CURRENTLY NOT IMPLEMENTED.    
     * @param   systemID    Entity's system ID.    
     * @return              The resolved source of the input stream, or <var>null</var> if unable to resolve.
     * @exception IOException   Thrown if unable to open the source defined by the specified IDs.
     * @see com.ibm.xml.parser.ExternalID
     * @see #closeInputStream
     * @see com.ibm.xml.parser.StreamProducer
     */
    public Source getInputStream(String name, String publicID, String systemID) throws IOException {
        URL u = null;
        try {
            u = new URL(this.url, systemID);
        } catch (MalformedURLException e) {
            printer.println("Stderr: Malformed URL: "+this.url+", "+systemID);
        }
        this.stack.push(this.url);
        this.url = u;
        if (null == u) {// || "file".equalsIgnoreCase(u.getProtocol())) {
            return new Source(new FileInputStream(systemID));
        } else {
            return new Source(u.openStream());
        }
    }

   /**
     * <p>Removes the input stream currently in use. 
     * <p>This method is defined by StreamProducer.
     * @param   source      CURRENTLY NOT IMPLEMENTED.
     * @see #getInputStream
     * @see com.ibm.xml.parser.StreamProducer
     */
    public void closeInputStream(Source source) {
        if (!this.stack.empty())
            this.url = (URL)this.stack.pop();
    }
}

