/*
* Copyright 2014 Jocki Hendry
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package simple.escp.json;
import simple.escp.dom.Report;
import simple.escp.Template;
import javax.json.Json;
import javax.json.JsonNumber;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonString;
import javax.json.JsonValue;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.logging.Logger;
/**
* This class represent a template in JSON format.
*
* <p>Example of basic JSON template:
*
* <pre>
* {
* "template": [
* "This is the first line",
* "This is the second line"
* ]
* }
* </pre>
*
* <p>Example of JSON template with placeholder:
*
* <pre>
* {
* "template": [
* "Your id is ${id}, Mr. ${nickname}."
* ]
* }
* </pre>
*
* <p>Example of JSON template with page format:
*
* <pre>
* {
* "pageFormat": {
* "lineSpacing": "1/8",
* "characterPitch": "17",
* "pageWidth": 30,
* "leftMargin": 5,
* "rightMargin": 5
* },
* "template": [
* "Your id is ${id}.",
* "Mr. ${nickname}."
* ]
* }
* </pre>
*
* <p>The value for <code>"template"</code> can be an array or an object. If it is an object,
* <code>"pageLength"</code> in <code>"pageFormat"</code> <strong>must</strong> be defined.
*
* <p>Object for <code>"template"</code> accept of the following keys: <code>"firstPage"</code>.
*
* <p>For example:
*
* <pre>
* {
* "pageFormat": {
* "pageLength": 10,
* },
* "template": {
* "firstPage": [
* "Welcome, ${nickname}. First-page only!"
* ],
* "detail": [
* "Your id is ${id}.",
* "Mr. ${nickname}."
* ]
* }
* }
* </pre>
*/
public class JsonTemplate extends Template {
private static final Logger LOG = Logger.getLogger("simple.escp");
private String originalText;
/**
* Create a new template from a string.
*
* @param json JSON formatted string
*/
public JsonTemplate(String json) {
this.originalText = json;
}
/**
* Create a new template from a JSON file with UTF-8 character set.
*
* @param file the file that will be read.
* @throws IOException if error occured when reading the file.
*/
public JsonTemplate(File file) throws IOException {
this.originalText = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
LOG.fine("JSON content: " + this.originalText);
}
/**
* Create a new template from a JSON file with custom character set.
*
* @param file the file that will be read.
* @param charset character set of the file.
* @throws IOException if error occured when reading the file.
*/
public JsonTemplate(File file, Charset charset) throws IOException {
this.originalText = new String(Files.readAllBytes(file.toPath()), charset);
LOG.fine("JSON content: " + this.originalText);
}
/**
* Create a new template from an URI.
*
* @param uri the URI to file that will be read.
* @throws IOException if error occured when reading the file.
*/
public JsonTemplate(URI uri) throws IOException {
this(new File(uri));
}
/**
* Create a new template from an <code>InputStream</code> that contains UTF-8 character set.
*
* @param inputStream the input stream that will be read.
* @throws IOException if error occured when reading the input stream.
*/
public JsonTemplate(InputStream inputStream) throws IOException {
this(inputStream, StandardCharsets.UTF_8);
}
/**
* Create a new template from an <code>InputStream</code>.
*
* @param inputStream the input stream that will be read.
* @param charset character set of the input stream.
* @throws IOException if error occured when reading the input stream.
*/
public JsonTemplate(InputStream inputStream, Charset charset) throws IOException {
InputStreamReader isr = new InputStreamReader(inputStream, charset);
StringWriter sw = new StringWriter();
int c;
while ((c = isr.read()) != -1) {
sw.write(c);
}
this.originalText = sw.getBuffer().toString();
LOG.fine("JSON content: " + this.originalText);
}
/**
* Get number from JSON number or JSON string.
*
* @param jsonValue <code>JsonValue</code> that will be parsed.
* @return a number represented by <code>jsonValue</code>.
*/
private Integer parseJsonNumber(JsonValue jsonValue) {
if (jsonValue.getValueType() == JsonValue.ValueType.NUMBER) {
return ((JsonNumber) jsonValue).intValue();
} else if (jsonValue.getValueType() == JsonValue.ValueType.STRING) {
return Integer.valueOf(((JsonString) jsonValue).getString());
}
LOG.warning("Can't convert " + jsonValue.toString() + " to number.");
throw new IllegalArgumentException("Can't convert " + jsonValue.toString() + " to number.");
}
/**
* Parse <code>"pageFormat"</code> section from this JSON template.
*
* @param json the root JSON of this template.
*/
private void parsePageFormat(JsonObject json) {
JsonObject parsedPageFormat = json.getJsonObject("pageFormat");
if (parsedPageFormat != null) {
// Line spacing
if (parsedPageFormat.containsKey("lineSpacing")) {
pageFormat.setLineSpacing(parsedPageFormat.getString("lineSpacing"));
}
// Character pitch
if (parsedPageFormat.containsKey("characterPitch")) {
pageFormat.setCharacterPitch(parsedPageFormat.getString("characterPitch"));
}
// Page length
if (parsedPageFormat.containsKey("pageLength")) {
pageFormat.setPageLength(parseJsonNumber(parsedPageFormat.get("pageLength")));
}
// Page width
if (parsedPageFormat.containsKey("pageWidth")) {
pageFormat.setPageWidth(parseJsonNumber(parsedPageFormat.get("pageWidth")));
}
// Left margin
if (parsedPageFormat.containsKey("leftMargin")) {
pageFormat.setLeftMargin(parseJsonNumber(parsedPageFormat.get("leftMargin")));
}
// Right margin
if (parsedPageFormat.containsKey("rightMargin")) {
pageFormat.setRightMargin(parseJsonNumber(parsedPageFormat.get("rightMargin")));
}
// Bottom margin
if (parsedPageFormat.containsKey("bottomMargin")) {
pageFormat.setBottomMargin(parseJsonNumber(parsedPageFormat.get("bottomMargin")));
}
// Type face
if (parsedPageFormat.containsKey("typeface")) {
pageFormat.setTypeface(parsedPageFormat.getString("typeface"));
}
// Auto line-feed
if (parsedPageFormat.containsKey("autoLineFeed")) {
pageFormat.setAutoLineFeed(parsedPageFormat.getBoolean("autoLineFeed"));
}
// Auto form-feed
if (parsedPageFormat.containsKey("autoFormFeed")) {
pageFormat.setAutoFormFeed(parsedPageFormat.getBoolean("autoFormFeed"));
}
// Use page length from printer
if (parsedPageFormat.containsKey("usePageLengthFromPrinter")) {
pageFormat.setUsePrinterPageLength(parsedPageFormat.getBoolean("usePageLengthFromPrinter"));
}
}
}
/**
* Parse <code>"template"</code> section from this JSON template.
* @param json the root JSON of this template.
* @return result in <code>Pages</code>.
*/
public Report parseTemplateText(JsonObject json) {
JsonValue template = json.get("template");
if (template == null) {
throw new IllegalArgumentException("JSON Template must contains 'template'.");
}
Parser parser = new Parser(getPageFormat());
if (template.getValueType() == JsonValue.ValueType.ARRAY) {
parser.setDetail(json.getJsonArray("template"));
} else if (template.getValueType() == JsonValue.ValueType.OBJECT) {
if (getPageFormat().getPageLength() == null) {
throw new IllegalArgumentException("Using object on 'template' require 'pageLength' " +
"to be defined in 'pageFormat'.");
}
JsonObject templateObject = json.getJsonObject("template");
if (templateObject.containsKey("firstPage")) {
parser.setFirstPage(templateObject.getJsonArray("firstPage"));
}
if (templateObject.containsKey("header")) {
parser.setHeader(templateObject.getJsonArray("header"));
}
if (templateObject.containsKey("footer")) {
parser.setFooter(templateObject.getJsonArray("footer"));
}
if (templateObject.containsKey("lastPageFooter")) {
parser.setLastPageFooter(templateObject.getJsonArray("lastPageFooter"));
}
if (templateObject.containsKey("lastPage")) {
parser.setLastPage(templateObject.getJsonArray("lastPage"));
}
if (templateObject.containsKey("detail")) {
parser.setDetail(templateObject.getJsonArray("detail"));
}
} else {
throw new IllegalArgumentException("Invalid value for 'template'.");
}
report = parser.parse();
return report;
}
/**
* {@inheritDoc}
*/
public Report parse() {
if (report == null) {
try (JsonReader reader = Json.createReader(new StringReader(originalText))) {
JsonObject json = reader.readObject();
LOG.fine("Parse pageFormat for [" + json + "]");
parsePageFormat(json);
LOG.fine("Parse template for [" + json + "]");
parseTemplateText(json);
}
}
return report;
}
/**
* Retrieve the text that represent this template before it is parsed.
*
* @return JSON string.
*/
public String getOriginalText() {
return originalText;
}
}