/*
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.json;
import java.io.IOException;
import java.io.InputStream;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.JsonToken;
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 AJSONGenerator extends ATreeGenerator {
private static byte[] initNextChar() {
byte[] results = new byte[128];
for (int i = 0; i < results.length; i++) {
results[i] = (byte) nextAllowedChar(((i & 0x7F) + 1) & 0x7F);
}
return results;
}
static final byte[] nextChar = initNextChar();
static final byte[] nextDigit = initNextDigit();
private static byte[] initNextDigit() {
byte[] results = new byte[10];
for (int i = 0; i < results.length; i++) {
results[i] = (byte) ('0' + ((i + 1) % 10));
}
return results;
}
private static int nextAllowedChar(int b) {
if (b <= 0x20) {
// if (b <= 0xD) {
// if (b <= 0xa) {
// if (b <= 0x9) {
// return (byte) 0x9;
// }
// return (byte) 0xA;
// }
// return (byte) 0xD;
// }
return (byte) 0x20;
}
// no backslash
if (b == '\\')
return b + 1;
// no quote
if (b == '"')
return b + 1;
return b;
}
private static class BoxedArray {
final byte[][] array;
final int start;
final int selector;
final byte character;
int end;
BoxedArray(byte[][] array, int selector, int start) {
this.array = array;
this.selector = selector;
this.start = start;
this.end = start;
this.character = array[selector][start];
}
void nextUnique() {
int pos = this.end;
while (pos >= this.start) {
byte r = this.array[this.selector][pos];
byte s = nextChar(r, 0);
this.array[this.selector][pos] = s;
if (s != this.character) {
return;
}
// s == this.character
// move up
pos--;
}
// if here we have to extend the buffer
byte[] replace = new byte[this.array[this.selector].length + 1];
System.arraycopy(this.array[this.selector], 0, replace, 0, this.start + 1);
System.arraycopy(this.array[this.selector], this.start, replace, this.start + 1,
replace.length - this.start - 1);
this.end++;
this.array[this.selector] = replace;
}
}
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 nextDigit(byte b, int incr) {
byte r = nextDigit[(b + incr) % 10];
return r;
}
/*
* @Generator(ext=FileExtension.JSON, type=Type.HIGH_NODE_NAME_SIZE,
* stype=SpecialType.STANDARD) public static class HighNodeNameSizeGenerator
* extends ATreeGenerator.ANodeNameSizeGenerator {
*
* protected HighNodeNameSizeGenerator(FileExtension ext, SpecialType
* xmlType) { super(ext, xmlType); }
*
* }
*/
// @Generator(ext=FileExtension.JSON, type=Type.HIGH_TEXT_SIZE,
// stype=SpecialType.STANDARD)
public static class HighTextSizeGenerator extends AHighTextSizeGenerator {
final byte[] start = s2b("{");
final byte[][] end = { s2b("}"), s2b("\"}") };
int choose_end = 0;
@Override
protected byte[] getEnd() {
return this.end[this.choose_end];
}
@Override
protected byte[] getStart() {
return this.start;
}
final byte[][] patterns = { s2b("\"\":\""), 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 1:
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.JSON, type = TreeType.HIGH_NODE_DENSITY, stype = SpecialType.STANDARD)
public static class HighDensityGenerator extends AHighDensityGenerator {
final byte[] start = s2b("{");
final byte[][] end = { s2b("}"), s2b("]}") };
int choose_end = 0;
@Override
protected byte[] getEnd() {
return this.end[this.choose_end];
}
@Override
protected byte[] getStart() {
return this.start;
}
// {} => 2 smallest
// {"A":1} => 7
// {"A":1,"B":1} => 13
// {"A":[1,1,1,1,1]}
// since key must be different at some point you need bigger key
// ..."AA":1, ...
// more or less 7 bytes per key value looks like the densest
private final byte[][] patterns = { // empty object is allowed
// "\"A\":1".getBytes(), // first used only once
// ",\"A\":1".getBytes()
s2b("\"\":["), // first used only once
s2b("{}"), s2b(",{}") };
// private final BoxedArray baA = new BoxedArray(this.patterns, 1, 2);
@Override
protected byte[][] getPatterns() {
return this.patterns;
}
@Override
protected int updatePattern(int current_pattern) {
if (current_pattern == 2)
return 2;
if (current_pattern == 1)
return 2;
if (current_pattern == 0)
return 1;
this.choose_end = 1;
return 0;
}
@Override
public byte[] applyVariation(Variation variation, byte[][] bs, int pos) {
// int incr = 0;
// IMPORTANT : the uniqueness is mandatory
// it doesn't depends on applyRandom hence
// if (pos == 1)
// this.baA.nextUnique();
switch (variation) {
case NO_VARIATION:
return bs[pos];
case RANDOM:
// incr = this.random.nextInt(10);
//$FALL-THROUGH$
case SEQUENTIAL:
switch (pos) {
case 0:
case 1:
// no op
break;
case 2:
// bs[pos][1] = nextDigit(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;
}
}
// high depth
// {} -> 2
// {"":{}} -> 7
// {"":{"":{}}} -> 12
// start "{"
// end "}"
// '"":{' and '}'
public abstract static class AHighDepthGenerator extends AHighNodeDepthGenerator {
@Override
protected int getPatternsLength() {
return getPatterns()[0].length + getPatterns()[1].length;
}
@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][1] = nextChar(bs[pos][1], incr);
break;
case 1:
// no op
break;
default:
}
return bs[pos];
default:
}
return null;
}
@Override
public IQuiXStreamReader getQuiXStreamReader() {
// TODO Auto-generated method stub
return null;
}
}
@TreeGenerator(ext = FileExtension.JSON, type = TreeType.HIGH_NODE_DEPTH, stype = SpecialType.STANDARD)
public static class HighDepthGeneratorObject extends AHighDepthGenerator {
final byte[] start = s2b("{");
final byte[] end = s2b("}");
@Override
protected byte[] getEnd() {
return this.end;
}
@Override
protected byte[] getStart() {
return this.start;
}
final byte[][] patterns = { s2b("\"\":{"), s2b("}") };
@Override
protected byte[][] getPatterns() {
return this.patterns;
}
@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.JSON, type = TreeType.HIGH_NODE_DEPTH, stype = SpecialType.ARRAY)
public static class HighDepthGeneratorArray extends AHighDepthGenerator {
final byte[] start = s2b("{\"\":");
final byte[] end = s2b("}");
@Override
protected byte[] getEnd() {
return this.end;
}
@Override
protected byte[] getStart() {
return this.start;
}
final byte[][] patterns = { s2b("["), s2b("]") };
@Override
protected byte[][] getPatterns() {
return this.patterns;
}
@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;
}
}
public static void main(String[] args)
throws JsonParseException, IOException, InstantiationException, IllegalAccessException {
/*
* final byte[][] patterns = { // empty object is allowed
*
* "\"A\":1".getBytes(), // first used only once ",\"A\":1".getBytes()
* }; BoxedArray baA = new BoxedArray(patterns, 1, 2); for (int i = 0; i
* <Integer.MAX_VALUE; i++) { baA.nextUnique(); }
*
*
* System.out.println(display(patterns[1]));
*/
JsonFactory f = new JsonFactory();
f.disable(Feature.ALLOW_COMMENTS);
f.disable(Feature.ALLOW_SINGLE_QUOTES);
// AGenerator generator = instance(ATreeGenerator.Type.HIGH_DENSITY);
AGenerator generator = instance(FileExtension.JSON, TreeType.HIGH_NODE_DEPTH, SpecialType.STANDARD);
InputStream is = generator.getInputStream(50, Unit.MBYTE, Variation.NO_VARIATION);
if (false) {
int c;
while ((c = is.read()) != -1) {
System.out.println(display((byte) (c & 0xFF)));
}
} else {
JsonParser p = f.createParser(is);
p.enable(Feature.STRICT_DUPLICATE_DETECTION);
while (p.nextToken() != JsonToken.END_OBJECT) {
//
}
}
}
}