All Downloads are FREE. Search and download functionalities are using the official Maven repository.

prompto.transpiler.HtmlGenerator Maven / Gradle / Ivy

There is a newer version: 0.1.57
Show newest version
package prompto.transpiler;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.security.InvalidParameterException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;

import prompto.code.ICodeStore;
import prompto.code.ModuleType;
import prompto.code.WebLibrary;
import prompto.declaration.CategoryDeclaration;
import prompto.declaration.IWidgetDeclaration;
import prompto.error.SyntaxError;
import prompto.grammar.Identifier;
import prompto.intrinsic.PromptoVersion;
import prompto.runtime.ApplicationContext;
import prompto.runtime.Context;
import prompto.store.DataStore;
import prompto.utils.HtmlUtils;
import prompto.utils.Logger;
import prompto.utils.YamlUtils;

public class HtmlGenerator {

	static final Logger logger = new Logger();
	
	String userAgent;
	Map pageConfig;
	String htmlEngine = null;
	Set headerEntries = new HashSet<>();
	
	public HtmlGenerator(String userAgent, Map pageConfig) {
		this.userAgent = userAgent;
		this.pageConfig = pageConfig;
	}

	public void generate(Context context, File htmlFile) throws IOException {
		try(OutputStream output = new FileOutputStream(htmlFile) ) {
			try(Writer writer = new OutputStreamWriter(output)) {
				try(PrintWriter printer = new PrintWriter(writer)) {
					generate(context, printer);
				}
			}
		}
	}

	private void generate(Context context, PrintWriter printer) throws IOException {
		generateProlog(printer);
		Consumer bodyWriter = generateHeader(context, printer);
		bodyWriter.accept(printer);
		generateEpilog(printer);
	}

	private void generatePageRenderScript(PrintWriter printer, String widgetName) {
		printer.println("");
	}

	private void generateProlog(PrintWriter printer) {
		printer.println("");
		printer.println("");
	}

	private Consumer generateHeader(Context context, PrintWriter printer) throws IOException {
		printer.println("");
		generateMetas(printer);
		generateTitle(printer);
		generateIcon(printer);
		generateName(printer);
		generatePromptoScripts(printer);
		try {
			generateLibraries(printer);
			Consumer bodyWriter = generatePageWidgetScript(context, printer);
			printer.println("");
			return bodyWriter;
		} catch(Exception e) {
			return p->generateException(p, e);
		}
	}
	
	@SuppressWarnings("unchecked")
	private void generateMetas(PrintWriter printer) {
		writeHeaderEntry(printer, "");
		Map config = getHeaderConfig();
		Object value = config.get("metas");
		if(value==null)
			return;
		else if(value instanceof Collection) {
			((Collection)value).forEach(item->{
				if(item instanceof String)
					writeHeaderEntry(printer, item);
				else
					logger.warn(()->"Expected a String, got " + value.getClass().getName());
			});
		} else
			logger.warn(()->"Expected a Collection, got " + value.getClass().getName());
	}

	private void writeHeaderEntry(PrintWriter printer, String entry) {
		if(headerEntries.add(entry))
			printer.println(entry);
		else
			logger.warn(()->"Duplicate header entry: " + entry);
	}

	private Consumer generatePageWidgetScript(Context context, PrintWriter printer) {
		try {
			String widgetName = getWidgetName();
			generatePageWidgetScript(context, printer, widgetName);
			return this::generateBody;
		} catch(SyntaxError e) {
			logger.error(()->"While generating page", e);
			return p->generateSyntaxError(p, e);
		} catch(Exception e) {
			return p->generateException(p, e);
		}
	}
	
	private void generateException(PrintWriter printer, Exception e) {
		printer.println("");
		e.printStackTrace(printer);
		printer.println("");
	}

	
	private void generateSyntaxError(PrintWriter printer, SyntaxError e) {
		printer.println("");
		printer.println("Syntax error '" + HtmlUtils.encodeHtmlEntities(e.getMessage()) + "'");
		printer.println("");
	}

	
	private String getWidgetName() {
		Map body = getBodyConfig();
		if(body==null)
			throw new SyntaxError("Missing 'body' section in page descriptor");
		Object value = body.get("widget");
		if(value instanceof String)
			return (String)value;
		if(value==null) 
			throw new SyntaxError("Missing 'widget' entry in 'body' section of page descriptor");
		else
			throw new SyntaxError("Expected a String for 'widget', got " + value.getClass().getName());
	}

	private void generatePageWidgetScript(Context context, PrintWriter printer, String widgetName) {
		IWidgetDeclaration widget = fetchWidgetDeclaration(context, widgetName);
		if(widget==null)
			throw new SyntaxError("No such widget '" + widgetName + "'");
		generatePageWidgetScript(printer, widget);
		generatePageRenderScript(printer, widgetName);
	}
	

	private IWidgetDeclaration fetchWidgetDeclaration(Context context, String widgetName) {
		CategoryDeclaration decl = context.getRegisteredDeclaration(CategoryDeclaration.class, new Identifier(widgetName));
		if(decl!=null && decl.isAWidget(context))
			return ((CategoryDeclaration)decl).asWidget();
		else
			return null;
		
	}

	private void generatePageWidgetScript(PrintWriter printer, IWidgetDeclaration declaration) {
		IJSEngine engine = IJSEngine.forUserAgent(userAgent);
		Context context = ApplicationContext.get().newLocalContext();
		Transpiler transpiler = new Transpiler(engine, context);
		declaration.declare(transpiler);
		if(transpiler.requires("DataStore")) {
			transpiler.require("Remote");
			transpiler.require("RemoteRunner");
			transpiler.require("RemoteStore");
			if(DataStore.getInstance().getNativeDbIdClass()==UUID.class)
				transpiler.require("UUID");
		}
		printer.println("");
		}
	}

	private void generatePromptoScripts(PrintWriter printer) {
		writeHeaderEntry(printer, "");
		writeHeaderEntry(printer, "");
		writeHeaderEntry(printer, "");
	}

	private void generateLibraries(PrintWriter printer) throws Exception {
		Map config = getHeaderConfig();
		if(config==null)
			return;
		generateWebLibraries(printer, config);
		generateWidgetLibraries(printer, config);
		generateHtmlEngine(printer, config);
		generateUiFramework(printer, config);
		generateStyleSheets(printer, config);
		generateJavaScripts(printer, config);
	}
	
	@SuppressWarnings("unchecked")
	private void generateWebLibraries(PrintWriter printer, Map config) throws Exception {
		Object value = config.get("webLibraries");
		if(value==null)
			return;
		else if(value instanceof Collection) {
			for(Object item : (Collection)value) {
				generateWebLibrary(printer, item);
			}
		} else
			logger.warn(()->"Expected a Collection, got " + value.getClass().getName());
	}

	private void generateWebLibrary(PrintWriter printer, Object entry) throws Exception {
		if(entry instanceof String)
			generateWebLibrary(printer, (String)entry, PromptoVersion.LATEST);
		else if(entry instanceof Map) {
			Object name = ((Map)entry).get("name");
			if(name instanceof String) {
				Object value = ((Map)entry).get("version");
				if(value instanceof String) try {
					PromptoVersion version = PromptoVersion.parse((String)value);
					generateWebLibrary(printer, (String)name, version);
				} catch(InvalidParameterException e) {
					throw new SyntaxError("Invalid 'version' in webLibraries entry: " + entry.toString());
				} else
					throw new SyntaxError("Missing 'version' in webLibraries entry: " + entry.toString());
			} else
				throw new SyntaxError("Missing 'name' in webLibraries entry: " + entry.toString());
		} else
			throw new SyntaxError("Could not understand webLibraries entry: " + entry.toString());
	}

	private void generateWebLibrary(PrintWriter printer, String name, PromptoVersion version) throws Exception {
		WebLibrary webLibrary = ICodeStore.getInstance().fetchVersionedModule(ModuleType.WEBLIBRARY, name, version);
		if(webLibrary == null)
			throw new SyntaxError("Could not find webLibrary: " + name + ", version: " + version.toString());
		Map config = new HashMap<>();
		config.put("htmlEngine", webLibrary.getHtmlEngine());
		config.put("javaScripts", webLibrary.getJavaScripts());
		config.put("styleSheets", webLibrary.getStyleSheets());
		generateHtmlEngine(printer, config);
		generateStyleSheets(printer, config);
		generateJavaScripts(printer, config);
	}

	@SuppressWarnings("unchecked")
	private void generateStyleSheets(PrintWriter printer, Map config) {
		Object value = config.get("styleSheets");
		if(value==null)
			return;
		else if(value instanceof Collection) {
			((Collection)value).forEach(item->{
				if(item instanceof String) {
					if(!(item.startsWith("http")|| item.startsWith("/")))
						item = "/" + item;
					String styleSheet = "";
					writeHeaderEntry(printer, styleSheet);
				} else {
					logger.warn(()->"Expected a String, got " + value.getClass().getName());
				}
			});
		} else
			logger.warn(()->"Expected a Collection, got " + value.getClass().getName());
	}

	@SuppressWarnings("unchecked")
	private void generateJavaScripts(PrintWriter printer, Map config) {
		Object value = config.get("javaScripts");
		if(value==null)
			return;
		else if(value instanceof Collection) {
			((Collection)value).forEach(item->{
				if(item instanceof String) {
					if(!(item.startsWith("http")|| item.startsWith("/")))
						item = "/" + item;
					String javaScript = "";
					writeHeaderEntry(printer, javaScript);
				} else {
					logger.warn(()->"Expected a String, got " + value.getClass().getName());
				}
			});
		} else
			logger.warn(()->"Expected a Collection, got " + value.getClass().getName());
	}

	private void generateUiFramework(PrintWriter printer, Map config) throws IOException {
		Object value = config.get("uiFramework");
		if(value==null)
			return;
		else if(value instanceof String) {
			config = YamlUtils.readResource("uiFrameworks/" + value + ".yaml");
			generateStyleSheets(printer, config);
			generateJavaScripts(printer, config);
		} else
			logger.warn(()->"Expected a String, got " + value.getClass().getName());
	}

	private void generateHtmlEngine(PrintWriter printer, Map config) throws Exception {
		Object value = config.get("htmlEngine");
		if(value==null)
			return;
		else if(value instanceof String) {
			if(value.equals(htmlEngine))
				return; // already generated
			else if(htmlEngine!=null)
				throw new SyntaxError("HTML engine: " + value + " conflicts with: " + htmlEngine);
			else {
				htmlEngine = (String)value;
				config = YamlUtils.readResource("htmlEngines/" + value + ".yaml");
				generateStyleSheets(printer, config);
				generateJavaScripts(printer, config);
			}
		} else
			logger.warn(()->"Expected a String, got " + value.getClass().getName());
	}

	private void generateWidgetLibraries(PrintWriter printer, Map config) throws Exception {
		Object values = config.get("widgetLibraries");
		if(values==null)
			return;
		else if(values instanceof Collection) {
			for(Object value : (Collection)values)
				generateWidgetLibrary(value, printer, config);
		} else
			logger.warn(()->"Expected a List, got " + values.getClass().getName());
	}

	private void generateWidgetLibrary(Object value, PrintWriter printer, Map config) throws Exception {
		if(value instanceof String) {
			config = YamlUtils.readResource("widgetLibraries/" + value + ".yaml");
			generateHtmlEngine(printer, config);
			generateUiFramework(printer, config);
			generateStyleSheets(printer, config);
			generateJavaScripts(printer, config);
		}
		else
			logger.warn(()->"Expected a String, got " + value.getClass().getName());
	}

	private void generateIcon(PrintWriter printer) {
		Map header = getHeaderConfig();
		if(header==null)
			return;
		Object value = header.get("icon");
		if(value==null)
			return;
		if(value instanceof String) {
			String icon = "";
			writeHeaderEntry(printer, icon);
		} else {
			logger.warn(()->"Expected a String, got " + value.getClass().getName());
		}
	}

	private void generateTitle(PrintWriter printer) {
		Map header = getHeaderConfig();
		if(header==null)
			return;
		Object value = header.get("title");
		if(value==null)
			return;
		else if(value instanceof String) {
			String title = "" + value + "";
			writeHeaderEntry(printer, title);
		} else {
			logger.warn(()->"Expected a String, got " + value.getClass().getName());
		}
	}

	private void generateName(PrintWriter printer) {
		Map header = getHeaderConfig();
		if(header==null)
			return;
		Object value = header.get("name");
		if(value==null)
			return;
		else if(value instanceof String) {
			String name = "";
			writeHeaderEntry(printer, name);
		} else {
			logger.warn(()->"Expected a String, got " + value.getClass().getName());
		}
	}

	private Map getHeaderConfig() {
		return getConfig(pageConfig, "header");
	}
	
	private Map getBodyConfig() {
		return getConfig(pageConfig, "body");
	}
	
	@SuppressWarnings("unchecked")
	private Map getConfig(Map source, String key) {
		Object value = source.get(key);
		if(value==null)
			return null;
		else if(value instanceof Map)
			return (Map)value;
		else {
			logger.warn(()->"Expected a Map, got " + value.getClass().getName());
			return null;
		}
	}

	private void generateBody(PrintWriter printer) {
		printer.println("");
		printer.println("
"); printer.println(""); printer.println("
"); printer.println(""); } private void generateEpilog(PrintWriter printer) { printer.println(""); } }