/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.struct;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
/*
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.events.DocumentEndEvent;
import org.yaml.snakeyaml.events.DocumentStartEvent;
import org.yaml.snakeyaml.events.Event;
import org.yaml.snakeyaml.events.MappingEndEvent;
import org.yaml.snakeyaml.events.MappingStartEvent;
import org.yaml.snakeyaml.events.ScalarEvent;
import org.yaml.snakeyaml.events.SequenceEndEvent;
import org.yaml.snakeyaml.events.SequenceStartEvent;
*/
import divconq.io.InputWrapper;
import divconq.io.UnicodeReader;
import divconq.json3.JsonParser;
import divconq.json3.JsonToken;
import divconq.lang.Memory;
import divconq.lang.op.FuncResult;
import divconq.lang.op.OperationResult;
import divconq.struct.builder.BuilderStateException;
import divconq.struct.builder.JsonStreamBuilder;
import divconq.struct.builder.ObjectBuilder;
import divconq.util.StringUtil;
import divconq.util.TimeUtil;
import divconq.xml.IParseHandler;
import divconq.xml.XNode;
import divconq.xml.XmlParser;
public class CompositeParser {
static public FuncResult<CompositeStruct> parseJson(CharSequence data) {
FuncResult<CompositeStruct> ret = new FuncResult<CompositeStruct>();
if (data == null) {
ret.error(1, "Error parsing JSON");
return ret;
}
try {
JsonParser jp = JsonParser.createParser(data);
ret.setResult(CompositeParser.parseJson(jp));
jp.close();
}
catch (Exception x) {
// TODO logging
//System.out.println("err: " + x);
ret.error(1, "Error parsing JSON");
}
return ret;
}
static public FuncResult<CompositeStruct> parseJson(Memory data) {
return CompositeParser.parseJson(new InputWrapper(data));
}
static public FuncResult<CompositeStruct> parseJson(InputStream data) {
FuncResult<CompositeStruct> ret = new FuncResult<CompositeStruct>();
try {
JsonParser jp = JsonParser.createParser(data);
ret.setResult(CompositeParser.parseJson(jp));
jp.close();
}
catch (Exception x) {
// TODO logging
//System.out.println("err: " + x);
ret.error(1, "Error parsing JSON");
}
return ret;
}
static public FuncResult<CompositeStruct> parseJson(Path path) {
FuncResult<CompositeStruct> ret = new FuncResult<CompositeStruct>();
try (InputStream in = Files.newInputStream(path)) {
try (JsonParser jp = JsonParser.createParser(in)) {
ret.setResult(CompositeParser.parseJson(jp));
}
catch (Exception x) {
ret.error("Error parsing JSON file " + path + ", error: " + x);
}
}
catch (IOException x) {
ret.error("Error reading file " + path + ", error: " + x);
}
return ret;
}
static public FuncResult<CompositeStruct> parseJsonUrl(String url) {
try {
return CompositeParser.parseJson(new URL(url));
}
catch (MalformedURLException x) {
FuncResult<CompositeStruct> ret = new FuncResult<CompositeStruct>();
ret.error("Error opening url " + url + ", error: " + x);
return ret;
}
}
static public FuncResult<CompositeStruct> parseJson(URL url) {
return CompositeParser.parseJson(url, null);
}
static public FuncResult<CompositeStruct> parseJson(URL url, RecordStruct hdrs) {
FuncResult<CompositeStruct> ret = new FuncResult<CompositeStruct>();
try {
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", "Mozilla/5.0");
con.setRequestProperty("Accept", "application/json");
if (hdrs != null)
for (FieldStruct fld : hdrs.getFields())
con.setRequestProperty(fld.getName(), fld.getValue().toString());
if (con.getResponseCode() != 200) {
ret.error("Error reading url " + url + ", error code: " + con.getResponseCode());
return ret;
}
try (InputStream in = con.getInputStream()) {
try (JsonParser jp = JsonParser.createParser(in)) {
ret.setResult(CompositeParser.parseJson(jp));
}
catch (Exception x) {
ret.error("Error parsing JSON url " + url + ", error: " + x);
}
}
catch (IOException x) {
ret.error("Error reading url " + url + ", error: " + x);
}
}
catch (IOException x) {
ret.error("Error opening url " + url + ", error: " + x);
}
return ret;
}
static public FuncResult<CompositeStruct> transactJson(CompositeStruct send, URL url) {
return CompositeParser.transactJson(send, url, null);
}
static public FuncResult<CompositeStruct> transactJson(CompositeStruct send, URL url, RecordStruct hdrs) {
FuncResult<CompositeStruct> ret = new FuncResult<CompositeStruct>();
try {
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("User-Agent", "Mozilla/5.0");
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Accept", "application/json");
if (hdrs != null)
for (FieldStruct fld : hdrs.getFields())
con.setRequestProperty(fld.getName(), fld.getValue().toString());
con.setDoOutput(true);
PrintStream pwr = new PrintStream(con.getOutputStream());
JsonStreamBuilder sb = new JsonStreamBuilder(pwr);
send.toBuilder(sb);
pwr.flush();
pwr.close();
if (con.getResponseCode() != 200) {
ret.error("Error reading url " + url + ", error code: " + con.getResponseCode());
return ret;
}
try (InputStream in = con.getInputStream()) {
try (JsonParser jp = JsonParser.createParser(in)) {
ret.setResult(CompositeParser.parseJson(jp));
}
catch (Exception x) {
ret.error("Error parsing JSON url " + url + ", error: " + x);
}
}
catch (IOException x) {
ret.error("Error reading url " + url + ", error: " + x);
}
}
catch (BuilderStateException x) {
ret.error("Error writing url " + url + ", error: " + x);
}
catch (IOException x) {
ret.error("Error opening url " + url + ", error: " + x);
}
return ret;
}
static public CompositeStruct parseJson(JsonParser jp) throws IOException, BuilderStateException {
ObjectBuilder eb = new ObjectBuilder();
JsonToken token = jp.nextToken();
while (token != null) {
if (token == JsonToken.START_ARRAY)
eb.startList();
else if (token == JsonToken.END_ARRAY)
eb.endList();
else if (token == JsonToken.START_OBJECT)
eb.startRecord();
else if (token == JsonToken.END_OBJECT)
eb.endRecord();
else if (token == JsonToken.FIELD_NAME)
eb.field(jp.getText()); // TODO check for bad chars (controls chars)
else if (token == JsonToken.VALUE_NULL)
eb.value(null);
else if (token == JsonToken.VALUE_FALSE)
eb.value(false);
else if (token == JsonToken.VALUE_TRUE)
eb.value(true);
else if (token == JsonToken.VALUE_STRING)
eb.value(jp.getText()); // TODO check for bad chars (controls chars)
else if (token == JsonToken.VALUE_NUMBER_FLOAT)
eb.value(jp.getDecimalValue());
else if (token == JsonToken.VALUE_NUMBER_INT)
eb.value(jp.getNumberValue()); //BigIntegerValue());
token = jp.nextToken();
}
return eb.getRoot();
}
/*
static public CompositeStruct parseYaml(CharSequence data) {
CompositeStruct ret = null;
try {
ret = CompositeParser.parseYaml(new StringReader(data.toString()));
}
catch (Exception x) {
// TODO logging
//System.out.println("err: " + x);
}
return ret;
}
static public CompositeStruct parseYaml(Memory data) {
return CompositeParser.parseYaml(new InputWrapper(data));
}
static public CompositeStruct parseYaml(InputStream data) {
CompositeStruct ret = null;
try {
ret = CompositeParser.parseYaml(new UnicodeReader(data));
}
catch (Exception x) {
// TODO logging
//System.out.println("err: " + x);
}
return ret;
}
static public CompositeStruct parseYaml(Reader rdr) throws IOException, BuilderStateException {
ObjectBuilder eb = new ObjectBuilder();
Yaml y = new Yaml();
boolean docaslist = false;
for (Event event : y.parse(rdr)) {
//System.out.println("Event: " + event);
if (event instanceof DocumentStartEvent) {
if (((DocumentStartEvent)event).getExplicit()) {
if (eb.getState() == BuilderState.Ready) {
docaslist = true;
eb.startList();
}
}
}
else if (event instanceof DocumentEndEvent) {
}
else if (event instanceof SequenceStartEvent)
eb.startList();
else if (event instanceof SequenceEndEvent)
eb.endList();
else if (event instanceof MappingStartEvent)
eb.startRecord();
else if (event instanceof MappingEndEvent)
eb.endRecord();
else if (event instanceof ScalarEvent) {
ScalarEvent sev = (ScalarEvent)event;
if (eb.getState() == BuilderState.InRecord)
eb.field(sev.getValue());
else {
//System.out.println(sev.getValue() + " - " + sev.getTag());
String tag = sev.getTag();
String val = sev.getValue();
if ("tag:yaml.org,2002:float".equals(tag))
eb.value(new BigDecimal(val));
else if ("tag:yaml.org,2002:int".equals(tag))
eb.value(new Long(val));
else if ("tag:yaml.org,2002:bool".equals(tag))
eb.value(new Boolean(val));
else if ("tag:yaml.org,2002:null".equals(tag))
eb.value(null);
else if ("tag:yaml.org,2002:str".equals(tag))
eb.value(val);
else if ("tag:yaml.org,2002:timestamp".equals(tag))
eb.value(TimeUtil.parseDateTime(val));
else { // TODO add support for dcSchema types
eb.value(val);
}
}
}
}
if (docaslist)
eb.endList();
return eb.getRoot();
}
*/
static public CompositeStruct parseXml(CharSequence data) {
CompositeStruct ret = null;
try {
ret = CompositeParser.parseXml(new StringReader(data.toString()));
}
catch (Exception x) {
// TODO logging
//System.out.println("err: " + x);
}
return ret;
}
static public CompositeStruct parseXml(Memory data) {
return CompositeParser.parseXml(new InputWrapper(data));
}
static public CompositeStruct parseXml(InputStream data) {
CompositeStruct ret = null;
try {
ret = CompositeParser.parseXml(new UnicodeReader(data));
}
catch (Exception x) {
// TODO logging
//System.out.println("err: " + x);
}
return ret;
}
static public CompositeStruct parseXml(Reader rdr) throws IOException, BuilderStateException {
XmlReader xrdr = new XmlReader(rdr);
try {
return xrdr.parse();
}
catch (Exception x) {
// TODO logging
//System.out.println("err: " + x);
}
return null;
}
/*
* XML
*
* <Record Type="nnn">
* <Field Name="Age" Type="nnn">
* <Scalar>9</Scalar>
* </Field>
* <Field Name="NickName" Value="Freckles" Type="nnn" />
* <Field Name="Friends">
* <List Type="nnn">
* <Scalar>Stacy</Scalar>
* <Scalar>Lenny</Scalar>
* </List>
* </Field>
* </Record>
*
*/
static public class XmlReader implements IParseHandler {
/**
* The source of the XML
*/
protected Reader input = null;
protected boolean inScalar = false;
protected String stype = null;
protected ObjectBuilder builder = new ObjectBuilder();
protected StringBuilder sb = null;
/**
* Set XML source to be a Reader
*
* @param input the XML source to be parsed
*/
public XmlReader(Reader input) {
this.input = input;
}
/* TODO wrong comments
* Parses the XML and returns the root element. Comments and PI
* will be missing, this is a really basic and lightweight XML utility.
*
* @return the root XML element
* @throws XMLParseException
* if an error occurs when parsing the XML
* @throws IOException
* if an error occurs when reading from the input source
*/
public CompositeStruct parse() throws Exception {
XmlParser.parse(this, this.input);
return this.builder.getRoot();
}
/*
* (non-Javadoc)
*
* @see IParseHandler#startDocument()
*/
@Override
public void startDocument(OperationResult or) {
}
/*
* (non-Javadoc)
*
* @see IParseHandler#endDocument()
*/
@Override
public void endDocument(OperationResult or) {
}
/*
* (non-Javadoc)
*
* @see IParseHandler#element(java.lang.String, java.lang.String, java.util.Map, int, int)
*/
@Override
public void element(OperationResult or, String tag, Map<String, String> attributes, int line, int col) {
try {
if ("Record".equals(tag)) {
this.builder.startRecord();
this.builder.endRecord();
}
else if ("List".equals(tag)) {
this.builder.startList();
this.builder.endList();
}
else if ("Field".equals(tag)) {
this.builder.field(attributes.get("Name"));
this.checkAttr(attributes);
}
else if ("Scalar".equals(tag)) {
this.checkAttr(attributes);
}
else if (this.inScalar) {
this.sb.append("<" + tag);
// Write the attributes out
for (Map.Entry<String, String> entry : attributes.entrySet()) {
this.sb.append(" " + entry.getKey() + "=");
this.sb.append("\"" + XNode.quote(entry.getValue()) + "\"");
}
this.sb.append(" />");
}
}
catch (BuilderStateException x) {
or.errorTr(246, x);
}
}
/*
* (non-Javadoc)
*
* @see IParseHandler#startElement(java.lang.String, java.lang.String, java.util.Map, int, int)
*/
@Override
public void startElement(OperationResult or, String tag, Map<String, String> attributes, int line, int col) {
try {
if ("Record".equals(tag))
this.builder.startRecord();
else if ("List".equals(tag))
this.builder.startList();
else if ("Field".equals(tag)) {
this.builder.field(attributes.get("Name"));
}
else if ("Scalar".equals(tag)) {
this.inScalar = true;
this.sb = new StringBuilder();
this.stype = attributes.get("Type");
}
else if (this.inScalar) {
this.sb.append("<" + tag);
// Write the attributes out
for (Map.Entry<String, String> entry : attributes.entrySet()) {
this.sb.append(" " + entry.getKey() + "=");
this.sb.append("\"" + XNode.quote(entry.getValue()) + "\"");
}
this.sb.append(">");
}
}
catch (BuilderStateException x) {
or.errorTr(246, x);
}
}
private void checkAttr(Map<String, String> attributes) throws BuilderStateException {
this.stype = attributes.get("Type");
if (attributes.containsKey("Value"))
this.scalarFinal(attributes.get("Value"));
}
private void scalarFinal(String value) throws BuilderStateException {
try {
if (value == null)
this.builder.value(null);
else {
if (StringUtil.isEmpty(value))
this.builder.value(null);
else if ("Decimal".equals(this.stype))
this.builder.value(new BigDecimal(value));
else if ("Integer".equals(this.stype))
this.builder.value(new Long(value));
else if ("Boolean".equals(this.stype))
this.builder.value(new Boolean(value));
else if ("Null".equals(this.stype))
this.builder.value(null);
else if ("String".equals(this.stype))
this.builder.value(value);
else if ("DateTime".equals(this.stype))
this.builder.value(TimeUtil.parseDateTime(value)); // TODO support more types
else
this.builder.value(value);
}
}
finally {
this.inScalar = false;
this.stype = null;
this.sb = null;
}
}
/*
* (non-Javadoc)
*
* @see IParseHandler#endElement(java.lang.String, java.lang.String)
*/
@Override
public void endElement(OperationResult or, String tag) {
try {
if ("Record".equals(tag))
this.builder.endRecord();
else if ("List".equals(tag))
this.builder.endList();
else if ("Scalar".equals(tag) && this.inScalar) { // treat as null
this.scalarFinal((this.sb == null) ? null : this.sb.toString());
}
else if (this.inScalar) {
// Now put the closing tag out
sb.append("</" + tag+ ">");
}
}
catch (BuilderStateException x) {
or.errorTr(246, x);
}
}
/*
* (non-Javadoc)
*
* @see IParseHandler#text(java.lang.String, boolean, int, int)
*/
@Override
public void text(OperationResult or, String str, boolean cdata, int line, int col) {
if (!this.inScalar)
return;
if (cdata)
this.sb.append("<![CDATA[" + str + "]]>");
else
this.sb.append(XNode.quote(str));
}
}
}