/*********************************************************************************
* The contents of this file are subject to the Common Public Attribution
* License Version 1.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.openemm.org/cpal1.html. The License is based on the Mozilla
* Public License Version 1.1 but Sections 14 and 15 have been added to cover
* use of software over a computer network and provide for limited attribution
* for the Original Developer. In addition, Exhibit A has been modified to be
* consistent with Exhibit B.
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is OpenEMM.
* The Original Developer is the Initial Developer.
* The Initial Developer of the Original Code is AGNITAS AG. All portions of
* the code written by AGNITAS AG are Copyright (c) 2007 AGNITAS AG. All Rights
* Reserved.
*
* Contributor(s): AGNITAS AG.
********************************************************************************/
package org.agnitas.backend;
import java.io.OutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class XMLWriter {
class Entity {
String name;
Entity parent;
protected Entity (String nName, Entity nParent) {
name = nName;
parent = nParent;
}
}
public class Creator {
String name;
ArrayList <String> vars;
ArrayList <String> vals;
private int count;
protected Creator (String nName) {
name = nName;
vars = new ArrayList <String> ();
vals = new ArrayList <String> ();
count = 0;
}
public void add (String var, Object val) {
vars.add (var);
vals.add (val.toString ());
++count;
}
public void add (Object ... param) {
int plen = param.length;
for (int n = 0; n < plen; n += 2) {
vars.add (param[n].toString ());
vals.add (param[n + 1].toString ());
}
}
public String[] getVariables () {
return vars.toArray (new String[count]);
}
public String[] getValues () {
return vals.toArray (new String[count]);
}
}
private StringBuffer buf;
private OutputStream out;
private Entity entity;
private int depth;
private boolean indentNext;
private void indent () {
if (indentNext) {
buf.append ('\n');
for (int n = 0; n < depth; ++n) {
buf.append (" ");
}
} else {
indentNext = true;
}
}
private static Pattern escaper = Pattern.compile ("[<>&'\"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u000b\u000c\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f]");
private void escape (String s) {
if (s != null) {
Matcher m = escaper.matcher (s);
int last;
last = 0;
while (m.find ()) {
int pos = m.start ();
if (pos > last) {
buf.append (s.substring (last, pos));
}
switch (s.charAt (pos)) {
case '<':
buf.append ("<");
break;
case '>':
buf.append (">");
break;
case '&':
buf.append ("&");
break;
case '\'':
buf.append ("'");
break;
case '"':
buf.append (""");
break;
default:
break;
}
last = pos + 1;
}
if (last == 0) {
buf.append (s);
} else if (last < s.length ()) {
buf.append (s.substring (last));
}
}
}
private static final String code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
private void encode (byte[] cont) {
int len;
int limit;
int count;
int i0, i1, i2;
len = cont.length;
limit = ((len + 2) / 3) * 3;
count = 0;
for (int n = 0; n < limit; n += 3) {
if (count == 0)
buf.append ('\n');
i0 = cont[n] & 0xff;
if (n + 1 < len) {
i1 = cont[n + 1] & 0xff;
if (n + 2 < len)
i2 = cont[n + 2] & 0xff;
else
i2 = 0;
} else
i1 = i2 = 0;
buf.append (code.charAt (i0 >>> 2));
buf.append (code.charAt (((i0 & 0x3) << 4) | (i1 >>> 4)));
if (n + 1 < len) {
buf.append (code.charAt (((i1 & 0xf) << 2) | (i2 >>> 6)));
if (n + 2 < len)
buf.append (code.charAt (i2 & 0x3f));
else
buf.append ("=");
} else
buf.append ("==");
count += 4;
if (count >= 76)
count = 0;
}
buf.append ('\n');
}
public XMLWriter (OutputStream destination) {
buf = new StringBuffer ();
out = destination;
entity = null;
depth = 0;
indentNext = true;
}
public void flush () throws IOException {
if (buf.length () > 0) {
out.write (buf.toString ().getBytes ("UTF8"));
out.flush ();
buf.setLength (0);
}
}
public void start () {
buf.setLength (0);
buf.append ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
}
public void end () throws IOException {
while (entity != null) {
close ();
}
buf.append ('\n');
flush ();
}
public void vopen (boolean simple, String name, String[] vars, String[] vals) {
indent ();
buf.append ("<");
buf.append (name);
if (vars != null) {
for (int n = 0; n < vars.length; ++n) {
String val = (vals != null) && (n < vals.length) ? vals[n] : "";
buf.append (" ");
buf.append (vars[n]);
buf.append ("=\"");
escape (val);
buf.append ("\"");
}
}
if (simple) {
buf.append ("/>");
} else {
buf.append (">");
entity = new Entity (name, entity);
++depth;
}
}
public void open (boolean simple, String name, Object ... param) {
int plen = param.length;
String[] vars = new String[plen >> 1];
String[] vals = new String[plen >> 1];
for (int n = 0; n < plen; n += 2) {
vars[n >> 1] = param[n].toString ();
vals[n >> 1] = param[n + 1].toString ();
}
vopen (simple, name, vars, vals);
}
public void opennode (String name, Object ... param) {
open (false, name, param);
}
public void openclose (String name, Object ... param) {
open (true, name, param);
}
public Creator create (String name, Object ... param) {
Creator c = new Creator (name);
c.add (param);
return c;
}
public void opennode (Creator c) {
vopen (false, c.name, c.getVariables (), c.getValues ());
}
public void openclose (Creator c) {
vopen (true, c.name, c.getVariables (), c.getValues ());
}
public void close (String name) {
boolean match = false;
while ((! match) && (entity != null)) {
match = (name == null) || entity.name.equals (name);
--depth;
indent ();
buf.append ("</");
buf.append (entity.name);
buf.append (">");
entity = entity.parent;
}
}
public void close () {
close ((String) null);
}
public void close (Creator c) {
close (c.name);
}
public void data (String s) {
escape (s);
indentNext = false;
}
public void data (byte[] b) {
encode (b);
indentNext = false;
}
public void single (String name, String content, Object ... param) {
if ((content != null) && (content.length () > 0)) {
opennode (name, param);
data (content);
close (name);
} else {
openclose (name, param);
}
}
public void single (String name, byte[] content, Object ... param) {
if ((content != null) && (content.length > 0)) {
opennode (name, param);
data (content);
close (name);
} else {
openclose (name, param);
}
}
public void single (String name, Object content, Object ... param) {
single (name, content.toString (), param);
}
public void single (Creator c, String content) {
if ((content != null) && (content.length () > 0)) {
opennode (c);
data (content);
close (c);
} else {
openclose (c);
}
}
public void single (Creator c, byte[] content) {
if ((content != null) && (content.length > 0)) {
opennode (c);
data (content);
close (c);
} else {
openclose (c);
}
}
public void single (Creator c, Object content) {
single (c, content.toString ());
}
public void comment (String s) {
indent ();
buf.append ("<!-- ");
buf.append (s);
buf.append (" -->");
}
public void empty () {
buf.append ('\n');
indentNext = true;
}
}