import org.xml.sax.*;
import java.io.*;
import java.net.URL;
import java.net.MalformedURLException;

/**
 * This tests an XML parser that has a SAX driver,
 * by generating the canonical XML for one or more XML files.
 * This is not written with performance in mind.
 *
 * @version $Revision: 1.5 $ $Date: 1998/05/14 01:45:35 $
 */
public class XMLTest extends HandlerBase {
  static public void main(String[] args) {
    try {
      if (args.length < 3) {
	System.err.println("usage: jview XMLTest driver-class output-directory file ...");
	System.exit(1);
      }
      Class cls = Class.forName(args[0]);
      File dir = new File(args[1]);
      if (!dir.isDirectory()) {
	System.err.println(args[1] + " is not a directory");
	System.exit(1);
      }
      for (int i = 2; i < args.length; i++) {
	File file =  new File(args[i]);
	if (file.isAbsolute()) {
	  System.err.println(args[i] + " is not a relative file");
	  System.exit(1);
	}

	Parser parser = (Parser)cls.newInstance();
	XMLTest tester = new XMLTest(new File(dir, args[i]));
	parser.setDocumentHandler(tester);
	parser.setErrorHandler(tester);
	try {
	  parser.parse(fileToURL(file).toString());
	}
	catch (SAXParseException e) {
	  tester.out.close();
	  tester.outFile.delete();
	}
	catch (SAXException e) {
	  System.err.println(args[i] + ": " + e.getMessage());
	}
      }
    }
    catch (ClassNotFoundException e) {
      System.err.println(e.toString());
    }
    catch (InstantiationException e) {
      System.err.println(e.toString());
    }
    catch (IllegalAccessException e) {
      System.err.println(e.toString());
    }
    catch (IOException e) {
      System.err.println(e.toString());
    }
  }
  

  static public URL fileToURL(File file) {
    String path = file.getAbsolutePath();
    String fSep = System.getProperty("file.separator");
    if (fSep != null && fSep.length() == 1)
      path = path.replace(fSep.charAt(0), '/');
    if (path.length() > 0 && path.charAt(0) != '/')
      path = '/' + path;
    try {
      return new URL("file", null, path);
    }
    catch (java.net.MalformedURLException e) {
      /* According to the spec this could only happen if the file
	 protocol were not recognized. */
      throw new Error("unexpected MalformedURLException");
    }
  }

  private OutputStream out;
  private File outFile;
  private StringBuffer buf = new StringBuffer();

  private XMLTest(File file) throws IOException {
    outFile = file;
    out = new BufferedOutputStream(new FileOutputStream(file));
  }

  public void startElement(String name, AttributeList atts) throws SAXException {
    try {
      flushChars();
      write("<");
      write(name);
      int len = atts.getLength();
      if (len > 0) {
	int[] v = new int[len];
	for (int i = 0; i < len; i++)
	  v[i] = i;
	/* Do an insertion sort. */
	for (int i = 1; i < len; i++) {
	  int n = v[i];
	  String s = atts.getName(n);
	  int j;
	  for (j = i - 1; j >= 0; j--) {
	    if (s.compareTo(atts.getName(v[j])) >= 0)
	      break;
	    v[j + 1] = v[j];
	  }
	  v[j + 1] = n;
	}
	for (int i = 0; i < len; i++) {
	  write(" ");
	  int n = v[i];
	  write(atts.getName(n));
	  write("=\"");
	  String value = atts.getValue(n);
	  int valueLen = value.length();
	  for (int j = 0; j < valueLen; j++)
	    appendChar(value.charAt(j));
	  flushChars();
	  write("\"");
	}
      }
      write(">");
    }
    catch (IOException e) {
      throw new SAXException(e);
    }
  }

  public void ignorableWhitespace(char[] cbuf, int start, int len) {
    characters(cbuf, start, len);
  }

  public void characters(char[] cbuf, int start, int len) {
    while (len-- > 0)
      appendChar(cbuf[start++]);
  }

  private void appendChar(char c) {
    switch (c) {
    case '&':
      buf.append("&amp;");
      break;
    case '<':
      buf.append("&lt;");
      break;
    case '>':
      buf.append("&gt;");
      break;
    case '"':
      buf.append("&quot;");
      break;
    case '\t':
      buf.append("&#9;");
      break;
    case '\n':
      buf.append("&#10;");
      break;
    case '\r':
      buf.append("&#13;");
      break;
    default:
      buf.append(c);
      break;
    }
  }

  public void endElement(String name) throws SAXException {
    try {
      flushChars();
      write("</");
      write(name);
      write(">");
    }
    catch (IOException e) {
      throw new SAXException(e);
    }
  }

  public void processingInstruction(String target, String data) throws SAXException {
    try {
      flushChars();
      write("<?");
      write(target);
      write(" ");
      write(data);
      write("?>");
    }
    catch (IOException e) {
      throw new SAXException(e);
    }
  }

  public void startDocument() { }

  public void endDocument() throws SAXException {
    try {
      flushChars();
      out.close();
    }
    catch (IOException e) {
      throw new SAXException(e);
    }
  }

  private void flushChars() throws IOException {
    if (buf.length() > 0) {
      write(buf.toString());
      buf.setLength(0);
    }
  }

  private void write(String s) throws IOException {
    int len = s.length();
    for (int i = 0; i < len; i++) {
      char c = s.charAt(i);
      if (c < 0x80)
	out.write(c);
      else {
	switch (c & 0xF800) {
	case 0:
	  out.write((((c >> 6) & 0x1F) | 0xC0));
	  out.write(((c & 0x3F) | 0x80));
	  break;
	case 0xD800:
	  char c2;
	  if (i + 1 < len
	      && (c & 0xFC00) == 0xD800
	      && ((c2 = s.charAt(i + 1)) & 0xFC00) == 0xDC00) {
	    ++i;
	    int n = ((c & 0x3FF) << 10) | (c2 & 0x3FF);
	    n += 0x10000;
	    out.write((((n >> 18) & 0x7) | 0xF0));
	    out.write((((n >> 12) & 0x3F) | 0x80));
	    out.write((((n >> 6) & 0x3F) | 0x80));
	    out.write(((n & 0x3F) | 0x80));
	    break;
	  }
	  /* this is an error situation really */
	  /* fall through */
	default:
	  out.write((((c >> 12) & 0xF) | 0xE0));
	  out.write((((c >> 6) & 0x3F) | 0x80));
	  out.write(((c & 0x3F) | 0x80));
	  break;
	}
      }
    }
  }
}
