/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. 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. */ package org.geogebra.common.io; import java.util.ArrayList; import org.geogebra.common.GeoGebraConstants; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.Macro; import org.geogebra.common.main.App; import org.geogebra.common.util.debug.Log; /** * Converts GeoGebra constructions to strings and vice versa */ public abstract class MyXMLio { /** * All xml output is zipped. The created zip archive contains an entry named * XML_FILE for the construction */ final public static String XML_FILE = "geogebra.xml"; /** * All xml output is zipped. The created zip archive contains an entry named * XML_FILE_MACRO for the macros */ final public static String XML_FILE_MACRO = "geogebra_macro.xml"; /** * defaults for 2D geos */ final public static String XML_FILE_DEFAULTS_2D = "geogebra_defaults2d.xml"; /** * defaults for 3D geos */ final public static String XML_FILE_DEFAULTS_3D = "geogebra_defaults3d.xml"; /** library JavaScript available to objects with JavaScript scripts */ final public static String JAVASCRIPT_FILE = "geogebra_javascript.js"; /** * All xml output is zipped. The created zip archive *may* contain an entry * named XML_FILE_THUMBNAIL for the construction */ final public static String XML_FILE_THUMBNAIL = "geogebra_thumbnail.png"; /** max no of horizontal pixels of thumbnail */ final public static double THUMBNAIL_PIXELS_X = 512.0; /** max no of vertical pixels of thumbnail */ final public static double THUMBNAIL_PIXELS_Y = 512.0; /** application */ protected App app; /** kernel */ protected Kernel kernel; /** construction */ protected Construction cons; /** handler for GGB files */ protected MyXMLHandler handler; /** * @param kernel * Kernel * @param cons * Construction */ public MyXMLio(Kernel kernel, Construction cons) { this.kernel = kernel; this.cons = cons; app = kernel.getApplication(); createXMLParser(); handler = getGGBHandler(); } /** * create XML parser */ abstract protected void createXMLParser(); /** * @return handler for GGB files */ protected MyXMLHandler getGGBHandler() { if (handler == null) { // ggb3D : to create also a MyXMLHandler3D // ggbDocHandler = new MyXMLHandler(kernel, cons); handler = kernel.newMyXMLHandler(cons); } return handler; } /** * Returns XML representation of all settings and construction needed for * undo. * * @param c * construction * @param getListenersToo * whether listeners (js) should be included * @return construction XML for undo step */ public final static synchronized StringBuilder getUndoXML(Construction c, boolean getListenersToo) { Kernel constructionKernel = c.getKernel(); boolean kernelIsGettingUndo = constructionKernel.isGettingUndo(); constructionKernel.setIsGettingUndo(true); App consApp = c.getApplication(); StringBuilder sb = new StringBuilder(); addXMLHeader(sb); addGeoGebraHeader(sb, false, consApp.getUniqueId()); // save euclidianView settings consApp.getCompanion().getEuclidianViewXML(sb, false); // save kernel settings c.getKernel().getKernelXML(sb, false); // save construction c.getConstructionXML(sb, getListenersToo); // save ProbabilityCalculator settings if (consApp.isUsingFullGui() && consApp.getGuiManager() != null && consApp.getGuiManager().hasProbabilityCalculator()) { consApp.getGuiManager().getProbabilityCalculatorXML(sb); } if (consApp.isUsingFullGui() && consApp.getGuiManager() != null && consApp.getGuiManager().hasSpreadsheetView()) { consApp.getGuiManager().getSpreadsheetViewXML(sb, false); } if (consApp.isUsingFullGui() && consApp.getGuiManager() != null && consApp.getGuiManager().hasDataCollectionView()) { consApp.getGuiManager().getDataCollectionViewXML(sb, false); } if (consApp.isUsingFullGui() && consApp.getGuiManager() != null && consApp.getGuiManager().hasAlgebraView()) { consApp.getGuiManager().getAlgebraViewXML(sb, false); } sb.append("</geogebra>"); constructionKernel.setIsGettingUndo(kernelIsGettingUndo); return sb; } /** * @param xml * XML string * @param clearConstruction * true to clear construction before processing * @param isGgtFile * true for macro files * @throws Exception * if XML is invalid or there was a problem while processing */ public void processXMLString(String xml, boolean clearConstruction, boolean isGgtFile) throws Exception { processXMLString(xml, clearConstruction, isGgtFile, true); } /** * @param xml * XML string * @param clearConstruction * true to clear construction before processing * @param isGgtFile * true for macro files * @param randomize * whether to randomize numbers * @throws Exception * if XML is invalid or there was a problem while processing */ public void processXMLString(String xml, boolean clearConstruction, boolean isGgtFile, boolean randomize) throws Exception { if (cons != null) { cons.setFileLoading(true); } processXMLString(xml, clearConstruction, isGgtFile, true, randomize); if (cons != null) { cons.setFileLoading(false); } } /** * Appends the <geogebra> tag to given builder, including XSD link and * construction ID * * @param sb * builder * @param isMacro * true for ggt files * @param uniqueId * construction ID */ public final static void addGeoGebraHeader(StringBuilder sb, boolean isMacro, String uniqueId) { // make sure File -> Share works in HTML5 App // (GeoGebraTube doesn't display 5.0 applets) String format = GeoGebraConstants.XML_FILE_FORMAT; sb.append("<geogebra format=\""); sb.append(format); sb.append("\" "); sb.append("version=\""); sb.append(GeoGebraConstants.VERSION_STRING); sb.append("\" "); if (uniqueId != null) { sb.append("id=\""); sb.append(uniqueId); // unique id to identify ggb file sb.append("\" "); } sb.append(" xsi:noNamespaceSchemaLocation=\"http://www.geogebra.org/"); if (isMacro) { sb.append(GeoGebraConstants.GGT_XSD_FILENAME); // eg ggt.xsd } else { sb.append(GeoGebraConstants.GGB_XSD_FILENAME); // eg ggb.xsd } sb.append( "\" xmlns=\"\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" >\n"); } /** * Appends <?xml ... ?> header to given builder * * @param sb * builder */ public final static void addXMLHeader(StringBuilder sb) { sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); } /** * @return XML representation of all settings and construction Returns XML * representation of all settings and construction. GeoGebra File * Format. */ public String getFullXML() { StringBuilder sb = new StringBuilder(); addXMLHeader(sb); addGeoGebraHeader(sb, false, app.getUniqueId()); // sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); // sb.append("<geogebra format=\"" + GeoGebra.XML_FILE_FORMAT + "\""); // sb.append(" // xsi:noNamespaceSchemaLocation=\"http://www.geogebra.org/"); // sb.append(GeoGebra.GGB_XSD_FILENAME); //eg ggb.xsd // sb.append("\" xmlns=\"\" // xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" >\n"); // save gui settings sb.append(app.getCompleteUserInterfaceXML(false)); // save construction cons.getConstructionXML(sb, false); sb.append("</geogebra>"); return sb.toString(); } /** * Returns XML representation of given macros and/or exercise in the kernel, * including header. * * @param macros * list of macros * @return XML representation of given macros in the kernel. */ public String getFullMacroXML(ArrayList<Macro> macros) { StringBuilder sb = new StringBuilder(); addXMLHeader(sb); addGeoGebraHeader(sb, true, null); // save construction sb.append(kernel.getMacroXML(macros)); sb.append(kernel.getExercise().getExerciseXML()); sb.append("</geogebra>"); return sb.toString(); } /** * Returns XML representation of all settings WITHOUT construction. * * @return XML representation of all settings WITHOUT construction. */ public String getPreferencesXML() { StringBuilder sb = new StringBuilder(); addXMLHeader(sb); addGeoGebraHeader(sb, false, null); // sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); // sb.append("<geogebra format=\"" + GeoGebra.XML_FILE_FORMAT // + "\">\n"); // save gui settings sb.append(app.getCompleteUserInterfaceXML(true)); sb.append("</geogebra>"); return sb.toString(); } /** * Returns .out representation for regression testing. * * @return .out representation for regression testing. */ public String getConstructionRegressionOut() { StringBuilder sb = new StringBuilder(); cons.getConstructionRegressionOut(sb); return sb.toString(); } /** * @param str * XML string * @param clearAll * true to clear construction before processing * @param isGGTOrDefaults * true for macro files and defaults * @param settingsBatch * true to process ettings changes as a batch * @param randomize * whether to randomize numbers afterwards * @throws Exception * if XML is invalid or there was a problem while processing */ final public void processXMLString(String str, boolean clearAll, boolean isGGTOrDefaults, boolean settingsBatch, boolean randomize) throws Exception { doParseXML(createXMLStreamString(str), clearAll, isGGTOrDefaults, clearAll, settingsBatch, randomize); } /** * @param stream * xml stream * @param clearConstruction * true to clear construction before processing * @param isGGTOrDefaults * true for macro files and defaults * @param mayZoom * true if reading the string may change the zoom * @param settingsBatch * true if we should use batch mode for settings * @param randomize * whether to randomize random numbers * @throws Exception * if a problem occurs */ final protected void doParseXML(XMLStream stream, boolean clearConstruction, boolean isGGTOrDefaults, boolean mayZoom, boolean settingsBatch, boolean randomize) throws Exception { boolean oldVal = kernel.isNotifyViewsActive(); boolean oldVal2 = kernel.isUsingInternalCommandNames(); kernel.setUseInternalCommandNames(true); if (!isGGTOrDefaults && mayZoom) { kernel.setNotifyViewsActive(false); } if (clearConstruction) { // clear construction kernel.clearConstruction(false); } try { kernel.setLoadingMode(true); if (settingsBatch && !isGGTOrDefaults) { app.getSettings().beginBatch(); parseXML(handler, stream); app.getSettings().endBatch(); } else { parseXML(handler, stream); } resetXMLParser(); kernel.setLoadingMode(false); } catch (Error e) { Log.error(e.getMessage()); if (!isGGTOrDefaults) { throw e; } } catch (Exception e) { Log.error(e.getMessage()); if (!isGGTOrDefaults) { throw e; } } finally { kernel.setUseInternalCommandNames(oldVal2); if (!isGGTOrDefaults && mayZoom) { kernel.updateConstruction(randomize, 1); kernel.setNotifyViewsActive(oldVal); } // #2153 if (!isGGTOrDefaults && cons != null && cons.hasSpreadsheetTracingGeos()) { // needs to be done after call to updateConstruction() to avoid // spurious traces app.getTraceManager().loadTraceGeoCollection(); } } // handle construction step stored in XMLhandler // do this only if the construction protocol navigation is showing if (!isGGTOrDefaults && oldVal && app.showConsProtNavigation()) { // ((GuiManagerD)app.getGuiManager()).setConstructionStep(handler.getConsStep()); if (app.getGuiManager() != null) { // if there is a ConstructionProtocolView, then update its // navigation bars app.getGuiManager().getConstructionProtocolView() .setConstructionStep(handler.getConsStep()); } else { // otherwise this is not needed app.getKernel().getConstruction() .setStep(handler.getConsStep()); } } } /** * reset XML parser */ abstract protected void resetXMLParser(); /** * parse XML string * * @param xmlHandler * handler * * @param stream * XML stream * @throws Exception * exception */ abstract protected void parseXML(MyXMLHandler xmlHandler, XMLStream stream) throws Exception; /** * @param perspectiveXML * string with <perspective> tag */ public void parsePerspectiveXML(String perspectiveXML) { try { MyXMLHandler h = getGGBHandler(); parseXML(h, createXMLStreamString(perspectiveXML)); } catch (Exception e) { e.printStackTrace(); } } /** * class for XML content streams (zip, buffers, String, etc.) * * @author mathieu * */ protected interface XMLStream { // tagging interface } /** * * @param str * XML string * @return XML stream for string */ abstract protected XMLStream createXMLStreamString(String str); /** * Reads zipped file from String that includes the construction saved in xml * format and maybe image files. * * @param zipFile * zip bytes * @throws Exception * when problem occurs */ abstract public void readZipFromString(byte[] zipFile) throws Exception; }