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

org.xdef.util.GUIEditor Maven / Gradle / Ivy

The newest version!
package org.xdef.util;

import org.xdef.XDConstants;
import org.xdef.XDDocument;
import org.xdef.XDFactory;
import org.xdef.XDOutput;
import org.xdef.XDPool;
import org.xdef.impl.XDSourceInfo;
import org.xdef.impl.XDSourceItem;
import org.xdef.impl.debug.GUIScreen;
import org.xdef.model.XMDefinition;
import org.xdef.model.XMElement;
import org.xdef.msg.SYS;
import org.xdef.sys.ArrayReporter;
import org.xdef.sys.Report;
import org.xdef.xml.KXmlUtils;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.xml.namespace.QName;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xdef.XDBuilder;
import org.xdef.impl.GenXDef;
import org.xdef.sys.FUtils;
import org.xdef.sys.SException;
import org.xdef.sys.SRuntimeException;
import org.xdef.xon.XonUtils;
import org.xdef.sys.SThrowable;
import org.xdef.sys.SUtils;

/** Provides interactive editing and debugging of X-definitions.
 * @author Vaclav Trojan
 */
public class GUIEditor extends GUIScreen {

	private static final Class[] URLPARAMS = new Class[]{URL.class};

	private static final XDPool PROJECTXDPOOL;
	static {
		String xdef = // X-definition of project description
"\n" +
"  \n" +
"\n" +
"    \n" +
"\n" +
"      \n" +
"        string(); /*this can be a file, url or XML with X-definition source*/\n"+
"      \n" +
"\n" +
"\n" +
"      \n" +
"        file() || url(); /*classpath item (url or pathname)*/\n"+
"      \n" +
"\n" +
"\n" +
"      \n" +
"\n" +
"\n" +
"      \n" +
"\n" +
"        \n" +
"\n" +
"          \n" +
"            string(); /*value of variable*/\n" +
"          \n" +
"\n" +
"\n" +
"          \n" +
"            string(); /*the file or XML data used as a context*/\n" +
"          \n" +
"\n" +
"\n" +
"          \n" +
"            string(); /*file with data*/\n" +
"          \n" +
"\n" +
"\n" +
"          \n" +
"        \n" +
"      \n" +
"\n" +
"      \n" +
"          file(); /*Directory where to store temporary data.*/\n" +
"      \n" +
"    \n" +
"  \n" +
"";
		PROJECTXDPOOL = XDFactory.compileXD(null, xdef);
	}

	/** Create instance of the screen to display the sources.
	 * @param si source information.
	 */
	private GUIEditor(final XDSourceInfo si) {super(si);}

	/** Initialize menu bar
	 * @param text the text of the JMenuItem
	 */
	private void initMenuBar(final String text) {
		JMenu fileMenu = _menuBar.add(new JMenu("File (F10)"));
		fileMenu.getActionMap().remove("Compile");
		_sourceArea.getActionMap().remove("Compile");
		JMenuItem ji;
		// Save as... menu item
		ji = new JMenuItem("Save as...");
		ji.setAccelerator(KeyStroke.getKeyStroke("control S"));
		ji.addActionListener((ActionEvent e) -> {
			if (updateSourceItem()) {
				_sourceItem._saved = true;
			}
			saveSource(_sourceItem);
		});
		fileMenu.add(ji);
		if (text != null) {
			fileMenu.addSeparator();
			ji = new JMenuItem(text);
			ji.setAccelerator(KeyStroke.getKeyStroke("F9"));
			ji.addActionListener((ActionEvent e) -> {
				if (((JMenuItem)e.getSource()).isEnabled()) {
					updateSourceItem();
					_actionFinished = false;
					notifyFrame();
				}
			});
		}
		fileMenu.add(ji);
		fileMenu.addSeparator();
		// Exit menu item
		ji = new JMenuItem("Exit");
		ji.setMnemonic((int) 'X');
		ji.addActionListener((ActionEvent e) -> {
			_actionFinished = true;
			if (_sourceItem != null && _sourceItem._changed) {
				String s;
				if (_sourceArea == null
					|| (s = _sourceArea.getText()) == null
					|| s.equals(_sourceItem._source)){
					_sourceItem._changed = false;
				}
			}
			notifyFrame();
		});
		fileMenu.add(ji);
		// Kill project
		ji = new JMenuItem("Kill process");
		ji.setMnemonic((int) 'K');
		ji.addActionListener((ActionEvent e) -> {
			_sourceItem._changed = false;
			_frame.dispose();
			_kill = true;
		});
		fileMenu.add(ji);
		// Source position info
		_menuBar.add(_sourcePositionInfo, BorderLayout.EAST);
		_frame.setJMenuBar(_menuBar);
		_sourceArea.getActionMap().remove("Compile");
		if (text != null) {
			_sourceArea.getActionMap().put(text,
				new AbstractAction(text){
				private static final long serialVersionUID =
					4377386270269629176L;
				@Override
				public void actionPerformed(ActionEvent evt) {
					updateSourceItem();
					_actionFinished = false;
					notifyFrame();
				}
			});
		}
		_sourceArea.setCaretPosition(0);
	}

	/** Display object.
	 * @param err Array of reported errors and messages.
	 * @param msg Text of header.
	 * @param obj Object to be displayed/
	 * @param si source information.
	 * @param editable true if editing is allowed.
	 * @param text the text of the JMenuItem
	 * @return source item.
	 * @throws Exception if an error occurs.
	 */
	private XDSourceItem display(final ArrayReporter err,
		final String msg,
		final Object obj,
		final XDSourceInfo si,
		final boolean editable,
		final String text) throws Exception {
		_windowName = msg;
		_si = si;
		XDSourceItem xsi = new XDSourceItem(obj);
		_sources = _si.getMap();
		_sources.clear();
		String sourceId;
		if (obj == null) {
			throw new SRuntimeException(SYS.SYS036, "Object is null");
		}
		if (obj instanceof File) {
			sourceId = ((File) obj).getCanonicalPath();
		} else if (obj instanceof String) {
			sourceId = "STRING";
		} else if (obj instanceof URL) {
			URL u = SUtils.getExtendedURL(((URL) obj).toExternalForm());
			sourceId = u.toExternalForm();
		} else {
			sourceId = obj.getClass().getName();
		}
		_sources.put(sourceId, xsi);
		_sourceItem = null;
		initSourceMap();
		initSourceWindow();
		setInitialSource();
		initMenuBar(text);
		if (err != null) {
			initInfoArea(err);
		}
		setLineNumberArea();
		if (!editable) {
			_sourceArea.setEditable(false);
			_infoArea.setRows(0);
			_infoArea.setVisible(false);
		}
		_frame.pack();
		_frame.setBounds(_si._xpos, _si._ypos, _si._width, _si._height);
		_frame.setVisible(true);
		waitFrame();
		si._xpos = _frame.getX();
		si._ypos = _frame.getY();
		si._width = _frame.getWidth();
		si._height = _frame.getHeight();
		closeEdit();
		if (_kill) {
			throw new Error("Process killed by user");
		}
		return xsi;
	}

	/** Display (and optionally edit) string.
	 * @param msg Text of header
	 * @param workDir directory for temporary files.
	 * @param deleteOnExit if true the new file is set to be deleted on exit.
	 * @param s string displayed.
	 * @param si source information.
	 * @param editable true if editing is allowed.
	 * @return source item.
	 * @throws Exception if an error occurs.
	 */
	private static XDSourceItem displayString(final String msg,
		final File workDir,
		final boolean deleteOnExit,
		final String s,
		final XDSourceInfo si,
		final boolean editable) throws Exception {
		Object o = s;
		if (editable && workDir != null) {
			o = new File(genTemporaryFile(s,workDir,"result.tmp",deleteOnExit));
		}
		XDSourceItem xsi =
			new GUIEditor(si).display(null, msg, o, si, editable, null);
		Map m = si.getMap();
		if (m.size() == 1) { //delete the file result.tmp if it was not saved
			if (!m.values().iterator().next()._saved) {
				new File(workDir, "result.tmp").delete();
			}
		}
		return xsi;
	}

	/** Display editable window with object (XML, JSON, text etc.).
	 * @param err ArrayReporter with reported errors and messages.
	 * @param msg Text of header.
	 * @param o Object with XML (may be Element, file etc.)
	 * @param si source information.
	 * @param text the text of the JMenuItem
	 * @return source item.
	 * @throws Exception if an error occurs.
	 */
	private static XDSourceItem editObject(final ArrayReporter err,
		final String msg,
		final Object o,
		final XDSourceInfo si,
		final String text) throws Exception {
		return new GUIEditor(si).display(err, msg, o, si, true, text);
	}

	/** Display and edit XMl given by the argument source.
	 * @param title title of window.
	 * @param source may be pathname of file, url or XML.
	 * @return source item of edited XML.
	 * @throws Exception if an error occurs.
	 */
	private static String editData(final String title,
		final String source) throws Exception {
		XDSourceInfo xi = new XDSourceInfo();
		Object o = source;
		String data = source.trim();
		String s = getSourceURL(data);
		File f = null;
		if (s != null) {
			URL u = new URL(s);
			String t = u.getFile();
			if (t != null) {
				f = new File(t);
				if (f.exists()) {
					o = f;
				}
			} else {
				f = new File(s);
				if (f.exists()) {
					o = f;
				} else if ((f = new File(data)).exists()) {
					o = f;
				} else {
					f = null;
					o = u;
				}
			}
		}
		XDSourceItem xsi = new XDSourceItem(o);
		xi.getMap().put(source, xsi);
		editObject(null, title, o, xi, null);
		if (xsi._saved || !source.equals(xsi._source)) {
			if (xsi._url != null && f != null) {
				if (new File(xsi._url.getFile()).getCanonicalFile().equals(
					f.getCanonicalFile())) {
					return data;
				}
				data = xsi._url.toExternalForm();
			} else {
				data = xsi._source.trim();
			}
		}
		return data;
	}

	/** Read input data or context.
	 * @param e element with data description.
	 * @param si Source information.
	 * @return string with data.
	 * @throws Exception if an error occurs.
	 */
	private static String getData(final Element e)
		throws Exception {
		if (e == null) {
			return null;
		}
		String data = e.getTextContent();
		if (data != null) {
			data = data.trim();
			if ("true".equals(e.getAttribute("Edit"))) {
				String s = editData("Input data", data);
				if (!data.equals(s)) {
					e.setTextContent(s);
					data = s;
				}
			}
		}
		return data;
	}

	/** Update list of XDefinitions in the project (assure the sequence
	 * from the map in XDSourceInfo).
	 * @param project project XML.
	 * @param si XDSourceInfo object.
	 */
	private static void updateXdefList(final Element project,
		final XDSourceInfo si) {
		NodeList nl = project.getElementsByTagName("XDefinition");
		for (int i = nl.getLength() - 1; i >= 0 ; i--) {
			project.removeChild(nl.item(i));  // romove all XDefinition items
		}
		Document doc = project.getOwnerDocument();
		for (String x: si.getMap().keySet()) {
			XDSourceItem xsi = si.getMap().get(x);
			String s = xsi._url!=null ? xsi._url.toExternalForm() : xsi._source;
			if (s != null && !(s = s.trim()).isEmpty()) {
				Element e = doc.createElement("XDefinition");
				e.setTextContent(s);
				project.appendChild(e);
			}
		}
	}

	/** Get canonized form of source item as the external form of URL.
	 * @param s source item.
	 * @return canonized form of source item as the external form of URL.
	 */
	private static String getSourceURL(final String s) {
		String t;
		if (s != null && !(t = s.trim()).isEmpty()) {
			try {
				File f = new File(t);
				if (f.exists()) {
					f = f.getCanonicalFile();
					return SUtils.getExtendedURL(
						f.toURI().toURL().toExternalForm()).toExternalForm();
				}
				return SUtils.getExtendedURL(t).toExternalForm();
			} catch (IOException ex) {}
		}
		return null;
	}

	/** Canonize project description.
	 * @param project Element with project description.
	 * @return canonized form of project description.
	 */
	private static Element canonizeProject(final Element project) {
		NodeList nl = project.getElementsByTagName("XDefinition");
		List sources = new ArrayList<>();
		for (int i = 0; i < nl.getLength(); i++) {
			Element e = (Element) nl.item(i);
			String s = e.getTextContent();
			if (s != null && !(s=s.trim()).isEmpty()) {
				String[] x;
				if (s.startsWith("<")) {
					x = new String[]{s};
				} else {
					try {
						x = SUtils.getSourceGroup(s);
					} catch (Exception ex) {
						x = new String[]{s};
					}
				}
				for (String t : x) {
					if (!sources.contains(t)) {
						sources.add(t);
					}
				}
			}
		}
		for (int i = nl.getLength() - 1; i >= 0; i--) {
			project.removeChild(nl.item(i));
		}
		Document doc = project.getOwnerDocument();
		for (String s: sources) {
			Element e = doc.createElement("XDefinition");
			e.setTextContent(s);
			project.appendChild(e);
		}
		// remove sources with the equal text content.
		List ar = new ArrayList<>();
		for (int i = 0; i < nl.getLength(); i++) {
			Element e = (Element) nl.item(i);
			String s = e.getTextContent();
			for (int j = i + 1; j < nl.getLength(); j++) {
				if (s.equals(((Element) nl.item(j)).getTextContent())){
					ar.add(e);
				}
			}
		}
		for (Element e: ar) {
			project.removeChild(e);
		}
		nl = project.getElementsByTagName("XDefinition");
		ar = new ArrayList<>();
		for (int i = 0; i < nl.getLength(); i++) {
			Element e = (Element) nl.item(i);
			ar.add(e);
		}
		nl = project.getElementsByTagName("Execute");
		for (int i = 0; i < nl.getLength(); i++) {
			NodeList nl1 = ((Element) nl.item(i)).getElementsByTagName("Input");
			for (int j = 0; j < nl1.getLength(); j++) {
				Element e = (Element) nl1.item(j);
				String s = getSourceURL(e.getTextContent());
				if (s != null) {
					e.setTextContent(s);
				}
			}
			nl1 = ((Element) nl.item(i)).getElementsByTagName("SaveResult");
			for (int j = 0; j < nl1.getLength(); j++) {
				Element e = (Element) nl.item(j);
				String s = getSourceURL(e.getAttribute("File"));
				if (s != null) {
					e.setAttribute("File", s);
				}
			}
		}
		return project;
	}

	/** Compare given element child elements in two elements.
	 * @param p1 first element.
	 * @param p2 second element.
	 * @param tagname name of child  elements to be compared.
	 * @return true if all child elements are equal.
	 */
	private static boolean compareNodes(final Element p1,
		final Element p2,
		final String tagname) {
		NodeList nl1,nl2;
		nl1 = p1.getElementsByTagName(tagname);
		nl2 = p2.getElementsByTagName(tagname);
		if (nl1.getLength() != nl2.getLength()) {
			return false;
		}
		ArrayReporter ar = new ArrayReporter();
		for (int i = 0; i < nl1.getLength(); i++) {
			Element el1 = (Element) nl1.item(i);
			Element el2 = (Element) nl2.item(i);
			KXmlUtils.compareElements(el1, el2, true, ar);
			if (!"OK".equals(ar.printToString())) {
				return false;
			}
			ar.clear();
		}
		return true;
	}

	/** Compare two elements with project description.
	 * @param p1 first project.
	 * @param p2 second project.
	 * @return true if both projects are equal.
	 */
	private static boolean compareProjects(final Element p1, final Element p2) {
		if (!p1.getAttribute("Show").equals(p2.getAttribute("Show"))) {
			return false;
		}
		if (!compareNodes(p1, p2, "Property")) {
			return false;
		}
		if (!compareNodes(p1, p2, "XDefinition")) {
			return false;
		}
		return compareNodes(p1, p2, "Execute");
	}

	@SuppressWarnings("unchecked")
	/** Run project with GUIEditor.
	 * @param param 'c' (compose) , 'v' (validate) or 'g' (generate)
	 * @param dataType 'x' (XML) or 'j' (XON/JSON)
	 * @param src source with the project.
	 */
	public static final void runEditor(final char param,
		final char dataType,
		final String src) {
		try {
			Element e;
			// Create element with project according to X-definition
			XDDocument pxd = PROJECTXDPOOL.createXDDocument();
			Element project = pxd.xparse(src, null);
			if ("true".equals(project.getAttribute("Show"))) {
				XDSourceInfo sinfo = new XDSourceInfo();
				Object o = src.charAt(0) == '<' ? src : new File(src);
				editObject(null, "Project", o, sinfo, null);
				project = pxd.xparse(src, null);
			}
			project  = canonizeProject(project);
			Element originalProject = (Element) project.cloneNode(true);
			NodeList nl;
			// set properties
			nl = project.getElementsByTagName("Property");
			Properties props = new Properties();
			for (int i = 0; i < nl.getLength(); i++) {
				e = (Element) nl.item(i);
				props.setProperty(e.getAttribute("Name"),
					e.getAttribute("Value"));
			}
			// add classspath items
			nl = project.getElementsByTagName("External");
			for (int i = 0; i < nl.getLength(); i++) {
				e = (Element) nl.item(i);
				String s = e.getTextContent();
				File f = new File(s);
				try {
					URL u = f.exists() ? f.toURI().toURL() : new URL(s);
					URLClassLoader sysloader =
						(URLClassLoader) ClassLoader.getSystemClassLoader();
					Class sysclass = URLClassLoader.class;
					Method method = sysclass.getDeclaredMethod(
						"addURL", URLPARAMS);
					method.setAccessible(true);
					method.invoke(sysloader, new Object[]{u});
				} catch (IllegalAccessException | IllegalArgumentException
					| NoSuchMethodException | SecurityException
					| InvocationTargetException | MalformedURLException ex) {
					throw new RuntimeException("Incorrect ClassPath: " + s);
				}
			}

			// compile X-definitions
			XDPool xp = compileProject(project, props);
			// execute project
			executeProject(project, xp, props);

			if (param == 'g') { // project was generated
				editData("Generated project", new File(src).exists()
					? src : KXmlUtils.nodeToString(project, true));
			} else if (!compareProjects(
				project = canonizeProject(project), originalProject)) {
				// something changed in the project; so ask to save it
				JFileChooser jf;
				if (src.charAt(0) != '<') {
					jf = new JFileChooser(src);
				} else {
					jf = new JFileChooser(new File(".").getCanonicalFile());
				}
				jf.setDialogTitle(
					"Project changed. Do you want to save the project?");
				jf.setToolTipText("Save THE PROJECT to a file");
				int retval = jf.showSaveDialog(null);
				jf.setEnabled(false);
				if (retval == JFileChooser.APPROVE_OPTION) {
					try {
						File f = jf.getSelectedFile();
						KXmlUtils.writeXml(f, "UTF-8", project, true, true);
					} catch (IOException ex) {
						JOptionPane.showMessageDialog(null,//Can't write
							Report.error(SYS.SYS036,"Can't write data to file: "
								+ jf.getSelectedFile() + "\n" + ex));
					}
				}
			}
		} catch (Exception ex) {
			if (ex instanceof SThrowable) {
				JOptionPane.showMessageDialog(null,
					((SThrowable) ex).getReport().toString());
			} else {
				JOptionPane.showMessageDialog(null, //Program exception &{0}
					Report.error(SYS.SYS036, ex.toString()).toString());
			}
		}
	}

	/** Compile project.
	 * @param project element with project.
	 * @param props properties.
	 * @throws Exception if an error occurs.
	 */
	private static XDPool compileProject(final Element project,
		final Properties props) throws Exception {
		// get X-definition sources
		NodeList nl = project.getElementsByTagName("XDefinition");
		try {
			String missingDefs = "";
				List xdefs = new ArrayList<>();
			for (int i = 0; i < nl.getLength(); i++) {
				Element e = (Element) nl.item(i);
				String t = e.getTextContent().trim();
				if (t != null && !t.isEmpty()) {
					if (t.charAt(0) == '<') {
						xdefs.add(t);
					} else { // file or grou of files
						String[] sources = SUtils.getSourceGroup(t);
						if (sources == null || sources.length == 0) {
							if (!missingDefs.isEmpty()) {
								missingDefs += '\n';
							}
							missingDefs += t;
						} else {
							for (String s: sources) {
								URL u = new URL(s);
								String pathname = u.getPath();
								File f = new File(pathname);
								if (f.exists() && f.isFile()) {
									xdefs.add(pathname);
								} else {
									if (!missingDefs.isEmpty()) {
										missingDefs += '\n';
									}
									missingDefs += pathname;
								}
							}
						}
					}
				}
			}
			if (!missingDefs.isEmpty()) {
				throw new RuntimeException(
					"UNAVAILABLE X-DEFINITION:\n\n"+
						missingDefs + "\nPLEASE CORRECT PROJECT DATA");
			}
			boolean changed = false;
			XDSourceInfo si;
			XDPool xp;
			for(;;) {
				ArrayReporter reporter = new ArrayReporter();
				XDBuilder builder = XDFactory.getXDBuilder(reporter, props);
				builder.setSource(xdefs.toArray(new String[0]));
				xp = builder.compileXD();
				si = xp.getXDSourceInfo();
				for (String x: si.getMap().keySet()) {
					XDSourceItem xsi = si.getMap().get(x);
					changed |= xsi._changed | xsi._saved;
				}
				if (changed) { //Update X-definitions elements
					updateXdefList(project, si);
				}
				if (reporter.errors()) {
					XDSourceItem xsi = null;
					for (XDSourceItem x: si.getMap().values()) {
						for (Report r: reporter) {
							if (x._url == null) {
								if (!missingDefs.isEmpty()) {
									missingDefs += '\n';
								}
								missingDefs += x._source;
							} else if (r.getModification().contains(
								x._url.toString())) {
								xsi = x;
								break;
							}
						}
						if (xsi != null) {
							break;
						}
					}
					if (!missingDefs.isEmpty()) {
						throw new RuntimeException(
							"UNAVAILABLE X-DEFINITION:\n\n"+
								missingDefs + "\nPLEASE CORRECT PROJECT DATA");
					}
					if (xsi == null) {
						Iterator it=
							si.getMap().values().iterator();
						if (it.hasNext()) {
							xsi = it.next();
						} else {
							throw new RuntimeException(
								reporter.printToString());
						}
					}
					GUIEditor ge = new GUIEditor(si);
					ge.display(reporter,
						"Error in X-definition", xsi._url, si, true, null);
					ge.closeEdit();
					reporter.clear();
					if (!changed) {
						return xp;
					}
				} else {
					return xp;
				}
			}
		} catch (Exception ex) {throw new RuntimeException(ex);}
	}

	@SuppressWarnings("unchecked")
	/** Execute project.
	 * @param project element with the project.
	 * @param xp compiled XDPool.
	 * @param props properties.
	 * @throws Exception if an error occurs.
	 */
	private static void executeProject(final Element project,
		final XDPool xp,
		final Properties props) throws Exception {
		Element e;
		NodeList nl;
		File workDir;
		boolean deleteOnExit;

		// create work directory
		nl = project.getElementsByTagName("WorkDir");
		File f = null;
		if (nl.getLength() != 0) {
			e = (Element) nl.item(0);
			f = new File(e.getTextContent());
		}
		workDir = getTempDir(f);
		deleteOnExit = !workDir.equals(f);

		// execute project
		nl = project.getElementsByTagName("Execute");
		XDSourceInfo si = xp.getXDSourceInfo();
		for (int i = 0; i < nl.getLength(); i++) {
			Element exe = (Element) nl.item(i);
			// get name of X-definition
			String xdName = exe.getAttribute("XDName").trim();
			// get data type
			String t = exe.getAttribute("DataType");
			char type = "JSON".equals(t) ? 'j' : "INI".equals(t) ? 'i' : 'x';
			// get input data
			e = KXmlUtils.firstElementChild(exe, "Input");
			String data = getData(e);
			// create XDDocument
			XDDocument xd = xp.createXDDocument(xdName);
			NodeList nl1 = KXmlUtils.getChildElements(exe, "Var");
			// set variables
			for (int j = 0; j < nl1.getLength(); j++) {
				e = (Element) nl1.item(j);
				String varName = e.getAttribute("Name");
				String value = e.getTextContent();
				xd.setVariable(varName, value);
			}
			// set context
			e = KXmlUtils.firstElementChild(exe,"Context");
			String context = getData(e);
			if (context != null) {
				xd.setXDContext(context);
			}
			ArrayReporter reporter = new ArrayReporter();
			// set properties
			xd.setProperties(props);
			// set stdout
			StringWriter strw = new StringWriter();
			XDOutput stdout =
				XDFactory.createXDOutput(new PrintWriter(strw), false);
			xd.setStdOut(stdout);
			String mode = exe.getAttribute("Mode");
			Object result;
			if ("construct".equals(mode)) { // run construction mode
				String name;
				String uri;
				XMDefinition def =  xp.getXMDefinition();
				XMElement[] x = xp.getXMDefinition().getModels();
				name = x[0].getName();
				uri = x[0].getNSUri();
				if (data != null && data.trim().length() > 0) {
					Element el = KXmlUtils.parseXml(data).
						getDocumentElement();
					xd.setXDContext(el);
					String n = el.getLocalName();
					String u = el.getNamespaceURI();
					if (def != null && def.getModel(u, n) != null) {
						uri = u;
						name = n;
					}
				}
				switch (type) {
					case 'i':
						result = xd.jcreate(name, reporter);
						break;
					case 'j':
						result = xd.jcreate(name, reporter);
						break;
					default:
						// type = x
						result = xd.xcreate(new QName(uri, name), reporter);
						break;
				}
			} else {  // run validation mode
				switch (type) {
					case 'i':
						result = xd.iparse(data, reporter);
						break;
					case 'j':
						result = xd.jparse(data, reporter);
						break;
					default:
						// type = x
						result = xd.xparse(data, reporter);
						break;
				}
			}
			// set bounds of the window from previous steps
			if (xd.getXDPool().getXDSourceInfo() != si) {
				XDSourceInfo si1 = xd.getXDPool().getXDSourceInfo();
				si1._xpos = si._xpos;
				si1._ypos = si._ypos;
				si1._width = si._width;
				si1._height = si._height;
				si = si1;
			}
			stdout.close();
			if (reporter.errorWarnings()) {
				if (data != null && "validate".equals(mode)) {
					// show result
					f = new File(new URL(data).getFile());
					XDSourceItem xsi = !f.isFile() || !f.exists()
						? editObject(reporter, "ERROR:", data, si,
							"Input data changed, run again?")
						: editObject(reporter, "ERROR:", f, si, null);
					if (xsi._saved && xsi._url != null) {
						if (JOptionPane.showConfirmDialog(null,
							"Input data changed, run again?",
							null, JOptionPane.OK_CANCEL_OPTION) == 0) {
							e = KXmlUtils.firstElementChild(exe, "Input");
							e.setTextContent(xsi._url.toExternalForm());
							i--;
							continue;
						}
					}
				} else { // error, display result, not editable
					displayString("ERROR:", null, false,
						reporter.printToString(), si, false);
				}
			}
			e = KXmlUtils.firstElementChild(exe, "SaveResult");
			if (e != null) { // save result XML
				String name = e.getAttribute("File").trim();
				boolean indent =
					"true".equals(e.getAttribute("Indent"));
				String encoding = e.getAttribute("Encoding");
				if (encoding.isEmpty()) {
					encoding = "UTF-8";
				}
				try {
					switch (type) {
						case 'i': {
							String s = XonUtils.toIniString(
								(Map) result);
							SUtils.writeString(new File(name), s, "ASCII");
							break;
						}
						case 'j': {
							String s = XonUtils.toJsonString(result, indent);
							SUtils.writeString(new File(name), s, "UTF-8");
							break;
						}
						default:
							// type = 'x'
							KXmlUtils.writeXml(new File(name), encoding,
								(Element) result, indent, true);
					}
				} catch (IOException | SException ex) {
//					//GUIEditor can't write XML data to file &{0}
//					JOptionPane.showMessageDialog(null,
//						Report.error(XDEF.XDEF851,name).toString());
				}
			}
			if ("true".equals(exe.getAttribute("DisplayResult"))) {
				String s;
				if (result == null) {
					s = "Result of process is null\n";
				} else {
					s = "=== Result of process ===\n";
					switch (type) {
						case 'i':
							// display as INI
							s += XonUtils.toIniString(
								(Map) result);
							break;
						case 'j':
							// display as JSON
							s += XonUtils.toJsonString(result, true);
							break;
						default:
							// display result XML
							s += KXmlUtils.nodeToString((Element) result, true);
					}
				}
				if (!strw.toString().isEmpty()) {
					s += s.isEmpty() ? "" : "\n";
					s += "\n=== System output ===\n" + strw.toString();
				}

				if (!s.isEmpty()) {// display result, allow editing
					displayString("Result of processing:",
						workDir, deleteOnExit, s, si, true);
				}
			}
		}
	}

	private static String genTemporaryFile(final String data,
		final File dir,
		final String name,
		final boolean deleteOnExit) {
		if (dir != null && dir.exists() && dir.isDirectory()) {
			try {
				File f = new File(dir, name);
				if (deleteOnExit) {
					f.deleteOnExit();
				}
				SUtils.writeString(f, data);
				return deleteOnExit ? f.getAbsolutePath() : f.getPath();
			} catch (SException ex) {}
		}
		throw new RuntimeException("Can't create file " + name +
			"to work direcory "+ dir);
	}

	private static File getTempDir(final File dir) {
		try {
			if (dir != null) {
				if (dir.exists() && dir.isDirectory()) {
					return dir;
				}
			} else {
				File tempDir = File.createTempFile("GUI", ".tmp");
				tempDir.delete();
				tempDir.mkdirs();
				tempDir.deleteOnExit();
				tempDir = new File(tempDir, "GUITemp");
				tempDir.mkdirs();
				tempDir.deleteOnExit();
				return tempDir;
			}
		} catch (IOException ex) {}
		throw new RuntimeException("Incorrect temp directory");
	}

	/** Call generation of a collection of X-definitions from a command line.
	 * @param args array with command line arguments:
	 * 
    *
  • -p file with the project *
  • -v [switches]run validation mode *
  • -c [switches]run construction mode *
  • -g [XML source]Generate X-definition form XML data *
* Switches: *
    *
  • -xdef file specifies the source with the X-definition *
  • -data filespecifies input data or context *
  • -debug sets the debug mode *
  • -editInput enables to runEditor input data before execution *
  • -displayResult displays result XML element *
*/ public static void main(String... args) { final String info = "Edit and run X-definition in graphical user interface.\n\n"+ "Command line arguments:\n"+ " -p project_file | -v [switches] |\n"+ " -c [switches] | -g [xml source] [-workDir dird]\n"+ "\n"+ " -p run a project file\n"+ " -v compile X-definition and runs validation mode\n"+ " -c compile X-definition and runs construction mode\n"+ " -g generate X-definition and project from input data (see switch -data).\n\n"+ "Switches:\n"+ " -xdef source with X-definition (input file or data; it may be\n"+ " specified more times)\n"+ " -external list of classpath items with external resources (may be filenames\n"+ " or urls).\n"+ " -format format of processed data (XML or JSON; default is XML)\n"+ " -data source (input file or data used for validation mode and as\n"+ " the context for construction mode or for generation of X-definition).\n"+ " -debug sets debugging mode when project is executed\n"+ " -editInput enables to edit input data before execution\n"+ " -displayResult displays result data\n"+ " -workDir directory where to store created data. This switch is optional;\n"+ " if not specified the work directory is created and deleted on exit."; if (args == null || args.length == 0) { System.err.println("No parameters.\n" + info); return; } String arg = args[0]; if ("-h".equals(arg)) { System.out.println(info); return; } if ("-p".equals(arg)) { switch (args.length) { case 1: System.err.println("Missing parameter with project\n"+info); break; case 2: runEditor('p', (char) 0, args[1]); break; default: System.err.println("More parameters not allowed here\n" + info); } return; } List xdefs = new ArrayList<>(); String dataPath = null; boolean wasDataPath = false; boolean wasXDefinition = false; String debug = null; String editInput = null; String displayResult = null; File workDir = null; List external = new ArrayList<>(); int i = 1; char param; char format = (char) 0; if (null == arg) { System.err.println("Incorrect parameter: " + arg + "\n" + info); return; } else switch (arg) { case "-c": param = 'c'; break; case "-v": param = 'v'; break; case "-g": // generate X-definition param = 'g'; if (args.length >= 2) { String x = args[1].trim(); if (!x.isEmpty() && x.charAt(0)!= '-') { i++; dataPath = x; } } break; default: System.err.println("Incorrect parameter: " + arg + "\n" + info); return; } while (i < args.length) { arg = args[i++]; switch(arg) { case "-xdef": if (param == 'g') { System.err.println( "Parameter -xdef not allowed with -g\n" + info); return; } wasXDefinition = true; xdefs.add(args[i++]); while (i < args.length && !args[i].startsWith("-")) { xdefs.add(args[i++]); } continue; case "-external": wasXDefinition = true; external.add(args[i++]); while (i < args.length && !args[i].startsWith("-")) { external.add(args[i++]); } continue; case "-format": if (format != 0) { System.err.println( "Redefinition of format parameter -format\n"+info); return; } String s = args[i++]; if (s.equalsIgnoreCase("JSON")) { format = 'j'; } else if (s.equalsIgnoreCase("XML")) { format = 'x'; } else if (s.equalsIgnoreCase("INI")) { format = 'i'; } else { System.err.println( "Incorrect parameter -format\n" + info); return; } continue; case "-data": if (wasDataPath) { System.err.println( "Redefinition of parameter \"-data\"\n" + info); return; } wasDataPath = true; dataPath = args[i++].trim(); continue; case "-debug": if (debug != null) { System.err.println( "Redefinition of parameter \"-debug\"\n" + info); return; } debug = "true"; continue; case "-editInput": if (editInput != null) { System.err.println( "Redefinition of parameter \"-editInput\"\n"+info); return; } editInput = "true"; continue; case "-displayResult": if (displayResult != null) { System.err.println( "Redefinition of parameter \"-displayResult\"\n" +info); return; } displayResult = "true"; continue; case "-workDir": if (workDir != null) { System.err.println( "Redefinition of parameter \"-tempDir\"\n" +info); return; } workDir = new File(args[i++]); } } String msg = ""; if (format == 0) { format = 'x'; // default is XML } File f = workDir; workDir = getTempDir(f); boolean deleteOnExit = !workDir.equals(f); String src = "\n"+ " \n" + " \n" + " \n"; switch (param) { case 'c': { // create if (xdefs.isEmpty()) { xdefs.add(genTemporaryFile( "\n" + "\n" + " " + " create \"Generated today message\"; \n" + " \n" + "

\n" + " create \"Hello! Today is" + " \"+now().toString(\"EEEE,d. MMMM GG yyyy, hh:mm a.\");\n" + "

\n" + "

\n" + " create \"This is an example of compose mode.\";\n" + "

\n" + " \n" + " create from(\"@x\");\n" + "
\n" + "
\n" + " \n" + "\n" + "
", workDir, "xdef.xml", deleteOnExit)); } if (!wasDataPath) { dataPath = genTemporaryFile( "\n \n \n", workDir, "data.xml", deleteOnExit); } src += " \n" + " \n"; src += dataPath + "\n\n"; src += " \n"; break; } case 'g': // generate X-definition try { if (!wasDataPath) { dataPath = genTemporaryFile( "\n" + "\n" + " text\n" + " \n" + "", workDir, "data.xml", deleteOnExit); } dataPath = editData("Input data", dataPath); Document d = KXmlUtils.parseXml(dataPath); Element w = d.createElement("W"); w.setTextContent(dataPath); String s = KXmlUtils.nodeToString(w, true).substring(3); s = s.substring(0, s.length()-4); dataPath = s.trim(); Element e = d.getDocumentElement(); Element xd = GenXDef.genXdef(e); xd.setAttribute("name", "test"); s = KXmlUtils.nodeToString(xd, true); xdefs.add(genTemporaryFile( s, workDir, "xdef.xml", deleteOnExit)); editInput = displayResult = "true"; } catch (Exception ex) { throw new RuntimeException(ex); } case 'v': { // validate if (xdefs.isEmpty()) { xdefs.add(genTemporaryFile( "\n" + (format == 'x' ? " \n" + " \n" + " ? string();\n" + " \n" + " \n" : format == 'j' ? "\n"+ "{\"a\": [\"occurs 3 int();\"]}\n"+ "\n" : "\n"+ "[\n"+ " [\"occurs + string();\"]\n"+ "]\n"+ "\n") + "", workDir, "xdef.xml", deleteOnExit)); if (displayResult == null) { displayResult = "true"; } } if (dataPath == null) { switch (format) { case 'i': dataPath = genTemporaryFile("a=1\nb=2", workDir, "data.ini", deleteOnExit); break; case 'x': dataPath = genTemporaryFile("\n" + " text\n" + " \n" + "", workDir, "data.xml", deleteOnExit); break; case 'j': dataPath = genTemporaryFile("{\"a\": [1, 2, 3]}", workDir, "data.json", deleteOnExit); break; default: break; } editInput = displayResult = "true"; } src += " \n"; src += " \n"; break; } } if (!msg.isEmpty()) { System.err.println(msg + "\n" + info); return; } // Sources for (String x: xdefs) { src += " " + x + "\n"; } // External items added to classPath for (String x: external) { f = new File(x); if (f.exists()) { src += " " + x + "\n"; continue; } else { try { src += " " + new URL(x).toExternalForm() + "\n"; continue; } catch (MalformedURLException ex) {} } throw new RuntimeException("Incorrect classpath: " + x); } if (!deleteOnExit) { // work directory was specified String s = SUtils.modifyString(workDir.getPath(), "&", "&"); src += " " + s + "\n"; } src += "
"; // run generated project runEditor(param, format, genTemporaryFile(src, workDir, "project.xml", deleteOnExit)); if (deleteOnExit) { JFileChooser jf = new JFileChooser(new File(".")); jf.setDialogTitle("Do you want to save the created project?"); jf.setToolTipText("Save THE PROJECT to a directory"); jf.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int retval = jf.showSaveDialog(null); jf.setEnabled(false); if (retval == JFileChooser.APPROVE_OPTION) { try { File dir = jf.getSelectedFile(); int ndx1, ndx2; ndx1 = src.indexOf("= 0) { ndx1 = src.indexOf('>', ndx1 + 6); ndx2 = src.indexOf("