/*
* Copyright (c) 1999-2000 by Simon St.Laurent. All Rights Reserved.
*
* This program is open source software; you may use, copy, modify, and
* redistribute it under the terms of the LICENSE with which it was
* originally distributed.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LICENSE for more details.
*/
//package com.simonstl.xml;
package schemacrawler.test.utility;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FilterReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
/**
* <p>
* This class adds a DOCTYPE to an incoming XML document or replaces its
* existing DOCTYPE declaration if it has one.
* </p>
*
* @author Simon St.Laurent
* @version 0.01 $Date: 2000/08/02 $
*/
public final class DOCTYPEChanger
extends FilterReader
{
/** This class defines a main() method to test the DOCTYPEChanger */
public static void main(final String[] args)
{
try
{
if (args.length != 1)
{
throw new IllegalArgumentException("Wrong number of arguments");
}
// Create a stream to read and clean the file
final DOCTYPEChanger tester = new DOCTYPEChanger(new FileReader(args[0]));
tester.setRootElement("html");
tester.setSystemIdentifier("http://www.simonstl.com/html");
tester.setPublicIdentifier("-//SIMONSTLCOM//DTD tester//EN");
tester.setInternalSubset("this is a test");
tester.setReplace(false);
try (final BufferedReader in = new BufferedReader(tester);)
{
String line;
while ((line = in.readLine()) != null)
{
System.out.println(line);
}
}
}
catch (final Exception e)
{
e.printStackTrace();
System.err.println("Usage: java DOCTYPEChanger <filename>");
}
}
protected boolean replace = true;
protected String rootElement;
protected String publicIdentifier = "";
protected String systemIdentifier = "";
protected String internalSubsetContent = "";
protected boolean docStarted = false;
protected boolean generating = false;
protected boolean cycle = true;
protected boolean internalSubset = false;
protected StringBuffer myBuffer = new StringBuffer();
/**
* This method is a placeholder - all 'real' activity appears in the
* int read() method. This placeholder is substantially from Java I/O
* by Elliotte Rusty Harold, http://www.oreilly.com/catalog/javaio/.
*/
private boolean endOfStream = false;
public DOCTYPEChanger(final InputStream in)
{
this(new InputStreamReader(in));
}
public DOCTYPEChanger(final Reader in)
{
super(new BufferedReader(in));
}
/**
* Returns the internal subset.
*/
public String getInternalSubset()
{
return internalSubsetContent;
}
/**
* Returns the public identifier. Mostly useful to see if you set it
* previously.
*/
public String getPublicIdentifier()
{
return publicIdentifier;
}
/**
* Returns the root element. Mostly useful to see if you set it
* previously.
*/
public String getRootElement()
{
return rootElement;
}
/**
* Returns the system identifier. Mostly useful to see if you set it
* previously.
*/
public String getSystemIdentifier()
{
return systemIdentifier;
}
@Override
public int read()
throws IOException
{
int c = 32;
if (myBuffer.length() == 0)
{
c = in.read();
if (c == 60 && docStarted == false)
{
// figure out if we have a DOCTYPE declaration
final int d = in.read();
switch (d)
{
case 63: // question mark, let it go
myBuffer.append((char) d);
break;
case 33: // either comment or DOCTYPE
int e = in.read();
if (e == 68)
{
// DOCTYPE! Bingo.
// INCLUDE/IGNORE are prohibited from
// internal subset, so we'll look for
// ]> and >.
if (replace)
{
while (cycle == true)
{
e = in.read();
if (e == 91)
{
internalSubset = true;
}
if (e == 62 && internalSubset == false)
{
// end of DOCTYPE
addDocType();
cycle = false;
}
if (e == 93)
{
internalSubset = false;
}
}// end while
}
else
{// end replace
myBuffer.append((char) d);
myBuffer.append((char) e);
}// end else
docStarted = true;
}
else
{// e didn't equal 68
myBuffer.append((char) d);
myBuffer.append((char) e);
} // end e==68
break;
default: // root element, need to insert in front
addDocType();
myBuffer.append("<");
myBuffer.append((char) d);
docStarted = true;
break;
}// end switch
}// end c==60, docStarted==false
}
else
{
c = feedFromInternalBuffer();
}
return c;
}
@Override
public int read(final char[] text, final int offset, final int length)
throws IOException
{
if (endOfStream)
{
return -1;
}
int numRead = 0;
for (int i = offset; i < offset + length; i++)
{
final int temp = this.read();
if (temp == -1)
{
endOfStream = true;
break;
}
text[i] = (char) temp;
numRead++;
}
return numRead;
}
/**
* Use this method to set the internal subset identified by the
* DOCTYPE declaration. If set, the result will be <!DOCTYPE
* <i>rootElement</i> <i>PUBLIC or SYSTEM identifiers</i>
* [<i>internalSubset</i>]>>
*/
public void setInternalSubset(final String subsetContents)
{
internalSubsetContent = subsetContents;
}
/**
* Use setPublicIdentifier to set the public identifer identified by
* the DOCTYPE declaration. If set, the result will be <!DOCTYPE
* <i>rootElement</i> PUBLIC '<i>publicIdentifier</i>'
* '<i>systemIdentifer</i>' <i>[internalSubset, if present]></i>> .
* You must also set a system identifier for this to work properly.
*/
public void setPublicIdentifier(final String identifier)
{
publicIdentifier = identifier;
}
/**
* Use setReplace to indicate whether to replace the DOCTYPE
* declarations for documents that already have one. False means don't
* replace, true means do replace.
*/
public void setReplace(final boolean replaceChoice)
{
replace = replaceChoice;
}
/**
* Use setRootElement to set the root element identified by the
* DOCTYPE declaration.
*/
public void setRootElement(final String elementName)
{
rootElement = elementName;
}
/**
* Use setSystemIdentifier to set the public identifer identified by
* the DOCTYPE declaration. If set without a public identifier, the
* result will be <!DOCTYPE <i>rootElement</i> SYSTEM
* '<i>systemIdentifer</i>' <i>[internalSubset, if present]></i>>
*/
public void setSystemIdentifier(final String identifier)
{
systemIdentifier = identifier;
}
protected void addDocType()
{
myBuffer.append("!DOCTYPE ");
myBuffer.append(rootElement);
if (publicIdentifier.equals(""))
{
if (!systemIdentifier.equals(""))
{
myBuffer.append(" SYSTEM '");
myBuffer.append(systemIdentifier);
myBuffer.append("'");
}
}
else
{
myBuffer.append(" PUBLIC '");
myBuffer.append(publicIdentifier);
myBuffer.append("' '");
myBuffer.append(systemIdentifier);
myBuffer.append("'");
}
myBuffer.append(" ");
if (!internalSubsetContent.equals(""))
{
myBuffer.append(" [\n");
myBuffer.append(internalSubsetContent);
myBuffer.append("\n]");
}
myBuffer.append(">\n");
docStarted = true;
}
protected int feedFromInternalBuffer()
{
// feed out the buffer a character at a time
final int retChar = myBuffer.charAt(0);
myBuffer.reverse();
myBuffer.setLength(myBuffer.length() - 1);
myBuffer.reverse();
return retChar;
}
}