/*
QuiXProc: efficient evaluation of XProc Pipelines.
Copyright (C) 2011-2015 Innovimax
All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.
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
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package innovimax.quixproc.datamodel.generator.xml;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.EnumSet;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import innovimax.quixproc.datamodel.event.AQuiXEvent;
import innovimax.quixproc.datamodel.generator.AGenerator;
import innovimax.quixproc.datamodel.generator.ATreeGenerator;
import innovimax.quixproc.datamodel.generator.annotations.TreeGenerator;
import innovimax.quixproc.datamodel.stream.IQuiXStreamReader;
public abstract class AXMLGenerator extends ATreeGenerator {
static final byte[] nextChar = initNextChar(false);
static final byte[] nextAttributeValue = initNextChar(true);
static final byte[] nextStartName = initNextName(true);
static final byte[] nextName = initNextName(false);
static final byte[] prevStartName = initPrevStartName();
private static int nextAllowedChar(int b, boolean attributeValue) {
if (b <= 0x20) {
if (b <= 0xD) {
if (b <= 0xa) {
if (b <= 0x9) {
return (byte) 0x9;
}
return (byte) 0xA;
}
return (byte) 0xD;
}
return (byte) 0x20;
}
// MUST
if (b == '<')
return b + 1;
if (b == '&')
return b + 1;
// MAY
if (b == '>')
return b + 1;
// attribute use quot
if (attributeValue && b == '"')
return b + 1;
return b;
}
private static int nextAllowedName(int b, boolean startName) {
// NameStartChar ::= "[A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] |
// [#xF8
// NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 |
// [#x0300-#x036F] | [#x203F-#x2040]
if (b <= 'A') {
if (!startName) {
if (b <= '-')
return '-';
if (b <= '.')
return '.';
if (b <= '0')
return '0';
if (b <= '9')
return b;
// FALL_TROUGH_ : return 'A'
}
return 'A';
}
if (b <= '_') {
if (b <= 'Z')
return b;
return '_';
}
if (b <= 'a') {
return 'a';
}
if (b < 0xC0) {
if (b <= 'z')
return b;
if (!startName) {
return '-';
}
return 'A';// 0xC0;
}
if (b < 0xD8) {
if (b <= 0xD6)
return b;
return 0xD8;
}
if (b < 0xF8) {
if (b <= 0xF6)
return b;
return 0xF8;
}
// b >= 0xF8
return b;
}
private static byte[] initNextChar(boolean attributeValue) {
byte[] results = new byte[128];
for (int i = 0; i < results.length; i++) {
results[i] = (byte) nextAllowedChar(((i & 0x7F) + 1) & 0x7F, attributeValue);
}
return results;
}
private static byte[] initNextName(boolean startName) {
byte[] results = new byte[128];
for (int i = 0; i < results.length; i++) {
results[i] = (byte) nextAllowedName(((i & 0x7F) + 1) & 0x7F, startName);
}
return results;
}
private static byte[] initPrevStartName() {
byte[] results = new byte[128];
for (int i = results.length; i > 0; i--) {
int r = nextAllowedName(((i & 0x7F) + 1) & 0x7F, true);
results[r] = (byte) i;
}
results[0x41] = (byte) 0x7a;
return results;
}
static byte nextChar(byte b, int incr) {
// System.out.println("nextChar : "+Integer.toHexString(b &
// 0xFF)+"("+Character.toString((char) (b& 0xFF))+")" );
byte r = nextChar[(b + incr) & 0x7F];
// System.out.println("nextChar : "+Integer.toHexString(r &
// 0xFF)+"("+Character.toString((char) (r& 0xFF))+")" );
return r;
}
static byte nextAttributeValue(byte b, int incr) {
// System.out.println("nextChar : "+Integer.toHexString(b &
// 0xFF)+"("+Character.toString((char) (b& 0xFF))+")" );
byte r = nextAttributeValue[(b + incr) & 0x7F];
// System.out.println("nextChar : "+Integer.toHexString(r &
// 0xFF)+"("+Character.toString((char) (r& 0xFF))+")" );
return r;
}
static byte nextStartName(byte b, int incr) {
// System.out.println("nextStartName : "+Integer.toHexString(b &
// 0xFF)+"("+Character.toString((char) (b& 0xFF))+")" );
byte r = b;
do {
r = nextStartName[r & 0x7F];
} while (incr-- > 0);
// System.out.println("nextStartName : "+Integer.toHexString(r &
// 0xFF)+"("+Character.toString((char) (r& 0xFF))+")" );
return r;
}
static byte nextName(byte b, int incr) {
// System.out.println("nextName : "+Integer.toHexString(b &
// 0xFF)+"("+Character.toString((char) (b& 0xFF))+")" );
byte r = nextName[(b + incr) & 0x7F];
// System.out.println("nextName : "+Integer.toHexString(r &
// 0xFF)+"("+Character.toString((char) (r& 0xFF))+")" );
return r;
}
static byte prevStartName(byte b, int incr) {
// System.out.println("prevStartName : "+Integer.toHexString(b &
// 0xFF)+"("+Character.toString((char) (b& 0xFF))+")" );
byte r = b;
do {
r = prevStartName[r & 0x7F];
} while (incr-- > 0);
// System.out.println("prevStartName : "+Integer.toHexString(r &
// 0xFF)+"("+Character.toString((char) (r& 0xFF))+")" );
return r;
}
@TreeGenerator(ext = FileExtension.XML, type = TreeType.HIGH_TEXT_SIZE, stype = SpecialType.STANDARD)
public static class HighTextSize extends AHighTextSizeGenerator {
final byte[] start = s2b("<r>");
final byte[] end = s2b("</r>");
@Override
protected byte[] getEnd() {
return this.end;
}
@Override
protected byte[] getStart() {
return this.start;
}
final byte[][] patterns = { s2b("a") };
@Override
protected byte[][] getPatterns() {
return this.patterns;
}
@Override
public byte[] applyVariation(Variation variation, byte[][] bs, int pos) {
int incr = 0;
switch (variation) {
case NO_VARIATION:
return bs[pos];
case RANDOM:
incr = this.random.nextInt(128);
//$FALL-THROUGH$
case SEQUENTIAL:
switch (pos) {
case 0:
bs[pos][0] = nextChar(bs[pos][0], incr);
break;
default:
}
return bs[pos];
default:
}
return null;
}
@Override
protected boolean notFinished(long current_size, int current_pattern, long total) {
return current_size < total;
}
@Override
protected int updatePattern(int current_pattern) {
return 0;
}
@Override
protected long updateSize(long current_size, int current_pattern) {
return current_size + 1;
}
@Override
public IQuiXStreamReader getQuiXStreamReader() {
// TODO Auto-generated method stub
return null;
}
@Override
protected boolean notFinishedEvent(long current_size, int current_pattern, long total) {
// TODO Auto-generated method stub
return false;
}
@Override
protected AQuiXEvent[] getEndEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[][] getPatternsEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[] getStartEvent() {
// TODO Auto-generated method stub
return null;
}
}
@TreeGenerator(ext = FileExtension.XML, type = TreeType.HIGH_NODE_DENSITY, stype = SpecialType.STANDARD)
public static class HighNodeDensityGenerator extends AHighDensityGenerator {
final byte[] start = s2b("<r>");
final byte[] end = s2b("</r>");
@Override
protected byte[] getEnd() {
return this.end;
}
@Override
protected byte[] getStart() {
return this.start;
}
final byte[][] patterns = { s2b("a"), s2b("<b/>") };
@Override
protected byte[][] getPatterns() {
return this.patterns;
}
@Override
public byte[] applyVariation(Variation variation, byte[][] bs, int pos) {
int incr = 0;
switch (variation) {
case NO_VARIATION:
return bs[pos];
case RANDOM:
incr = this.random.nextInt(128);
//$FALL-THROUGH$
case SEQUENTIAL:
switch (pos) {
case 0:
bs[pos][0] = nextChar(bs[pos][0], incr);
break;
case 1:
bs[pos][1] = nextStartName(bs[pos][1], incr);
break;
default:
}
return bs[pos];
default:
}
return null;
}
@Override
public IQuiXStreamReader getQuiXStreamReader() {
// TODO Auto-generated method stub
return null;
}
@Override
protected boolean notFinishedEvent(long current_size, int current_pattern, long total) {
// TODO Auto-generated method stub
return false;
}
@Override
protected AQuiXEvent[] getEndEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[][] getPatternsEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[] getStartEvent() {
// TODO Auto-generated method stub
return null;
}
}
@TreeGenerator(ext = FileExtension.XML, type = TreeType.HIGH_NODE_DEPTH, stype = SpecialType.STANDARD)
public static class HighNodeDepthGenerator extends AHighNodeDepthGenerator {
final byte[] start = s2b("<r>");
final byte[] end = s2b("</r>");
@Override
protected byte[] getEnd() {
return this.end;
}
@Override
protected byte[] getStart() {
return this.start;
}
final byte[][] patterns = { s2b("<a>"), s2b("</a>") };
@Override
protected byte[][] getPatterns() {
return this.patterns;
}
@Override
protected int getPatternsLength() {
int result = this.patterns[0].length + this.patterns[1].length;
// System.out.println("get patterns lenght " +result);
return result;
}
private boolean isReturn = false;
boolean directionForward = true;
@Override
public byte[] applyVariation(Variation variation, byte[][] bs, int pos) {
int incr = 0;
switch (variation) {
case NO_VARIATION:
return bs[pos];
case RANDOM:
// how to have reversible random ?
incr = this.random.nextInt(128, this.directionForward);
//$FALL-THROUGH$
case SEQUENTIAL:
switch (pos) {
case 0:
bs[pos][1] = nextStartName(bs[pos][1], incr);
// System.out.println("+"+incr);
this.isReturn = true;
break;
case 1:
if (this.isReturn) {
this.isReturn = false;
bs[pos][2] = bs[0/* take the previous */][1];
// resetRandom();
this.random.prev();
this.directionForward = false;
} else {
// System.out.println("-"+incr);
bs[pos][2] = prevStartName(bs[pos][2], incr);
}
break;
default:
}
return bs[pos];
default:
}
return null;
}
@Override
public IQuiXStreamReader getQuiXStreamReader() {
// TODO Auto-generated method stub
return null;
}
@Override
protected boolean notFinishedEvent(long current_size, int current_pattern, long total) {
// TODO Auto-generated method stub
return false;
}
@Override
protected AQuiXEvent[] getEndEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[][] getPatternsEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[] getStartEvent() {
// TODO Auto-generated method stub
return null;
}
}
@TreeGenerator(ext = FileExtension.XML, type = TreeType.HIGH_NODE_DEPTH, stype = SpecialType.NAMESPACE)
public static class HighDepthNamespaceGenerator extends AHighNodeDepthGenerator {
final byte[] start = s2b("<r>");
final byte[] end = s2b("</r>");
@Override
protected byte[] getEnd() {
return this.end;
}
@Override
protected byte[] getStart() {
return this.start;
}
final byte[][] patterns = { s2b("<a xmlns=\"a\">"), s2b("</a>") };
@Override
protected byte[][] getPatterns() {
return this.patterns;
}
@Override
protected int getPatternsLength() {
return this.patterns[0].length + this.patterns[1].length;
}
private boolean isReturn = false;
boolean directionForward = true;
@Override
public byte[] applyVariation(Variation variation, byte[][] bs, int pos) {
int incr = 0, incr2 = 0;
switch (variation) {
case NO_VARIATION:
return bs[pos];
case RANDOM:
incr = this.random.nextInt(128, this.directionForward);
incr2 = this.random.nextInt(128, this.directionForward);
// System.out.println(incr + ","+incr2);
//$FALL-THROUGH$
case SEQUENTIAL:
switch (pos) {
case 0:
bs[pos][1] = nextStartName(bs[pos][1], incr);
// System.out.println("+"+incr);
bs[pos][10] = nextAttributeValue(bs[pos][10], incr2);
this.isReturn = true;
break;
case 1:
if (this.isReturn) {
this.isReturn = false;
bs[pos][2] = bs[0/* previous */][1];
// resetRandom();
this.random.prev();
this.random.prev();
this.directionForward = false;
} else {
// System.out.println("-"+incr2);
bs[pos][2] = prevStartName(bs[pos][2], incr2);
}
break;
default:
}
return bs[pos];
default:
}
return null;
}
@Override
public IQuiXStreamReader getQuiXStreamReader() {
// TODO Auto-generated method stub
return null;
}
@Override
protected boolean notFinishedEvent(long current_size, int current_pattern, long total) {
// TODO Auto-generated method stub
return false;
}
@Override
protected AQuiXEvent[] getEndEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[][] getPatternsEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[] getStartEvent() {
// TODO Auto-generated method stub
return null;
}
}
@TreeGenerator(ext = FileExtension.XML, type = TreeType.HIGH_NODE_NAME_SIZE, stype = SpecialType.STANDARD)
public static class HighElementNameSizeSingle extends ANodeNameSizeGenerator {
@Override
protected byte[] getEnd() {
return s2b("/>");
}
@Override
protected byte[][] getPatterns() {
byte[][] result = { s2b("a") };
return result;
}
@Override
protected byte[] getStart() {
return s2b("<_");
}
@Override
protected boolean notFinished(long current_size, int current_pattern, long total) {
return current_size < total;
}
@Override
protected int updatePattern(int current_pattern) {
return 0;
}
@Override
protected long updateSize(long current_size, int current_pattern) {
return current_size + 1;
}
@Override
public byte[] applyVariation(Variation variation, byte[][] bs, int pos) {
int incr = 0;
switch (variation) {
case NO_VARIATION:
return bs[pos];
case RANDOM:
incr = this.random.nextInt(128);
//$FALL-THROUGH$
case SEQUENTIAL:
bs[pos][0] = nextName(bs[pos][0], incr);
return bs[pos];
default:
}
return null;
}
@Override
public IQuiXStreamReader getQuiXStreamReader() {
// TODO Auto-generated method stub
return null;
}
@Override
protected boolean notFinishedEvent(long current_size, int current_pattern, long total) {
// TODO Auto-generated method stub
return false;
}
@Override
protected AQuiXEvent[] getEndEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[][] getPatternsEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[] getStartEvent() {
// TODO Auto-generated method stub
return null;
}
}
@TreeGenerator(ext = FileExtension.XML, type = TreeType.HIGH_NODE_NAME_SIZE, stype = SpecialType.OPEN_CLOSE)
public static class HighElementNameSizeOpenClose extends ANodeNameSizeGenerator {
@Override
protected byte[] getEnd() {
return s2b(">");
}
private final byte[][] patterns = { s2b("a"), s2b("></_"), s2b("a") };
@Override
protected byte[][] getPatterns() {
return this.patterns;
}
@Override
protected byte[] getStart() {
return s2b("<_");
}
private long loop = 0;
@Override
protected boolean notFinished(long current_size, int current_pattern, long total) {
// System.out.println(current_size + ", "+current_pattern+",
// "+total);
if (current_size + this.patterns[1].length < total) {
this.loop++;
return true;
}
// current_size >= total
if (current_pattern <= 0) {
// switch pattern
this.next_pattern = 1;
return true;
}
if (current_pattern == 1) {
// switch pattern
this.next_pattern = 2;
}
// next_pattern will be 2
return this.loop-- > 0;
}
int next_pattern = 0;
@Override
protected int updatePattern(int current_pattern) {
return this.next_pattern;
}
@Override
protected long updateSize(long current_size, int current_pattern) {
return current_size + (current_pattern == 0 ? 2 : 0);
}
@Override
public byte[] applyVariation(Variation variation, byte[][] bs, int pos) {
int incr = 0;
switch (variation) {
case NO_VARIATION:
return bs[pos];
case RANDOM:
incr = this.random.nextInt(128);
//$FALL-THROUGH$
case SEQUENTIAL:
switch (pos) {
case 0:
bs[0][0] = nextName(bs[0][0], incr);
break;
case 1:
// NOP
resetRandom();
break;
case 2:
bs[2][0] = nextName(bs[2][0], incr);
break;
default:
}
return bs[pos];
default:
}
return null;
}
@Override
public IQuiXStreamReader getQuiXStreamReader() {
// TODO Auto-generated method stub
return null;
}
@Override
protected boolean notFinishedEvent(long current_size, int current_pattern, long total) {
// TODO Auto-generated method stub
return false;
}
@Override
protected AQuiXEvent[] getEndEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[][] getPatternsEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
protected AQuiXEvent[] getStartEvent() {
// TODO Auto-generated method stub
return null;
}
}
private static void call(TreeType gtype, SpecialType special, int size, Unit unit)
throws IOException, XMLStreamException, InstantiationException, IllegalAccessException {
AGenerator generator = instance(FileExtension.XML, gtype, special);
call(generator, gtype.name(), size, unit);
}
private static void call(AGenerator generator, String gtypename, int size, Unit unit)
throws IOException, XMLStreamException {
long start = System.currentTimeMillis();
if (USE_STREAM) {
InputStream is = generator.getInputStream(size, unit, VARIATION);
switch (PROCESS) {
case READ_CHAR:
int c;
while ((c = is.read()) != -1) {
// System.out.println(display((byte) (c & 0xFF)));
}
break;
case READ_BUFFER:
byte[] b = new byte[1024];
while ((c = is.read(b)) != -1) {
// System.out.println(display((byte) (c & 0xFF)));
}
break;
case PARSE:
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(is);
while (xsr.hasNext()) {
xsr.next();
}
break;
default:
}
} else {
File f = new File(
"/Users/innovimax/tmp/quixdm/" + gtypename.toLowerCase() + "-" + size + unit.display() + ".xml");
generator.generate(f, size, unit, VARIATION);
System.out.print("File : " + f.getName() + "\tSize : " + f.length() + "\t\t");
}
System.out.println("Time : " + (System.currentTimeMillis() - start));
}
public static void main(String[] args) throws FileNotFoundException, IOException, XMLStreamException,
InstantiationException, IllegalAccessException {
System.out.println("nextChar\t: " + display(nextChar));
System.out.println("nextAttributeValue\t: " + display(nextAttributeValue));
System.out.println("nextStartName\t: " + display(nextStartName));
System.out.println("nextName\t: " + display(nextName));
System.out.println("prevStartName\t:" + display(prevStartName));
if (ONE_INSTANCE) {
// call(ATreeGenerator.Type.HIGH_NODE_DENSITY, null, 150,
// Unit.MBYTE);
call(TreeType.HIGH_NODE_DEPTH, null, 201, Unit.MBYTE);
// call(ATreeGenerator.Type.HIGH_NODE_DEPTH, null, 112, Unit.MBYTE);
} else {
for (TreeType gtype : EnumSet.of(TreeType.HIGH_NODE_NAME_SIZE, TreeType.HIGH_NODE_NAME_SIZE,
TreeType.HIGH_NODE_DENSITY, TreeType.HIGH_NODE_DEPTH)) {
for (SpecialType stype : SpecialType.allowedModifiers(FileExtension.XML, gtype)) {
for (Unit unit : EnumSet.of(Unit.BYTE, Unit.KBYTE, Unit.MBYTE, Unit.GBYTE)) {
int[] values = { 1, 2, 5, 10, 20, 50, 100, 200, 500 };
for (int i : values) {
if (unit == Unit.GBYTE && i > 1)
continue;
call(gtype, stype, i, unit);
}
}
}
}
}
}
protected static final boolean USE_STREAM = true;
protected static final Process PROCESS = Process.READ_BUFFER;
protected static final boolean ONE_INSTANCE = true;
protected static final Variation VARIATION = Variation.SEQUENTIAL;
public enum Process {
READ_CHAR, READ_BUFFER, PARSE
}
}