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

org.fife.ui.rtextarea.Macro Maven / Gradle / Ivy

/*
 * 09/16/2004
 *
 * Macro.java - A macro as recorded/played back by an RTextArea.
 *
 * This library is distributed under a modified BSD license.  See the included
 * LICENSE file for details.
 */
package org.fife.ui.rtextarea;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.fife.io.UnicodeReader;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;


/**
 * A macro as recorded/played back by an {@link RTextArea}.

* * Macros are static; when a Macro is loaded, it can be run by any * instance of RTextArea in the application. To activate and play * back a macro, use the following methods: * *

    *
  • {@link RTextArea#loadMacro(Macro)} *
  • {@link RTextArea#playbackLastMacro()} *
* * To record and save a new macro, you'd use the following methods: * *
    *
  • {@link RTextArea#beginRecordingMacro()} (this discards the previous * "current" macro, if any) *
  • {@link RTextArea#endRecordingMacro()} (at this point, you could call * playbackLastMacro() to play this macro immediately if * desired) *
  • {@link RTextArea#getCurrentMacro()}.{@link #saveToFile(File)} *
* * As Macros save themselves as XML files, a common technique is * to save all macros in files named "{@link #getName()}.xml", and * place them all in a common directory. * * @author Robert Futrell * @version 0.1 */ public class Macro { private String name; private ArrayList macroRecords; private static final String ROOT_ELEMENT = "macro"; private static final String MACRO_NAME = "macroName"; private static final String ACTION = "action"; private static final String ID = "id"; private static final String UNTITLED_MACRO_NAME = ""; private static final String FILE_ENCODING = "UTF-8"; /** * Constructor. */ public Macro() { this(UNTITLED_MACRO_NAME); } /** * Loads a macro from a file on disk. * * @param file The file from which to load the macro. * @throws IOException If the file does not exist or an I/O exception occurs * while reading the file. * @see #saveToFile(String) * @see #saveToFile(File) */ public Macro(File file) throws IOException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; Document doc; try { db = dbf.newDocumentBuilder(); //InputSource is = new InputSource(new FileReader(file)); InputSource is = new InputSource(new UnicodeReader( new FileInputStream(file), FILE_ENCODING)); is.setEncoding(FILE_ENCODING); doc = db.parse(is);//db.parse(file); } catch (Exception e) { e.printStackTrace(); String desc = e.getMessage(); if (desc==null) { desc = e.toString(); } throw new IOException("Error parsing XML: " + desc); } macroRecords = new ArrayList<>(); // Traverse the XML tree. boolean parsedOK = initializeFromXMLFile(doc.getDocumentElement()); if (!parsedOK) { name = null; macroRecords.clear(); macroRecords = null; throw new IOException("Error parsing XML!"); } } /** * Constructor. * * @param name The name of the macro. */ public Macro(String name) { this(name, null); } /** * Constructor. * * @param name The name of the macro. * @param records The initial records of the macro. */ public Macro(String name, List records) { this.name = name; if (records!=null) { macroRecords = new ArrayList<>(records.size()); macroRecords.addAll(records); } else { macroRecords = new ArrayList<>(10); } } /** * Adds a macro record to this macro. * * @param record The record to add. If null, nothing happens. * @see #getMacroRecords */ public void addMacroRecord(MacroRecord record) { if (record!=null) { macroRecords.add(record); } } /** * Returns the macro records that make up this macro. * * @return The macro records. * @see #addMacroRecord */ public List getMacroRecords() { return macroRecords; } /** * Returns the name of this macro. A macro's name is simply something to * identify it with in a UI; it has nothing to do with the name of the file * to save the macro to. * * @return The macro's name. * @see #setName(String) */ public String getName() { return name; } /** * Used in parsing an XML document containing a macro. This method * initializes this macro with the data contained in the passed-in node. * * @param root The root node of the parsed XML document. * @return true if the macro initialization went okay; * false if an error occurred. */ private boolean initializeFromXMLFile(Element root) { /* * This method expects the XML document to be in the following format: * * * * test * abcdefg * [...] * ... * * */ NodeList childNodes = root.getChildNodes(); int count = childNodes.getLength(); for (int i=0; i0) { node = childNodes2.item(0); int type2 = node.getNodeType(); if (type2!=Node.CDATA_SECTION_NODE && type2!=Node.TEXT_NODE) { return false; } name = node.getNodeValue().trim(); } //System.err.println("Macro name==" + name); } else if (nodeName.equals(ACTION)) { NamedNodeMap attributes = node.getAttributes(); if (attributes==null || attributes.getLength()!=1) { return false; } Node node2 = attributes.item(0); MacroRecord macroRecord = new MacroRecord(); if (!node2.getNodeName().equals(ID)) { return false; } macroRecord.id = node2.getNodeValue(); NodeList childNodes2 = node.getChildNodes(); int length = childNodes2.getLength(); if (length==0) { // Could be empty "" command. //System.err.println("... empty actionCommand"); macroRecord.actionCommand = ""; //System.err.println("... adding action: " + macroRecord); macroRecords.add(macroRecord); break; } else { node = childNodes2.item(0); int type2 = node.getNodeType(); if (type2!=Node.CDATA_SECTION_NODE && type2!=Node.TEXT_NODE) { return false; } macroRecord.actionCommand = node.getNodeValue(); macroRecords.add(macroRecord); } } break; default: break; // Skip whitespace nodes, etc. } } // Everything went okay. return true; } /** * Saves this macro to an XML file. This file can later be read in by the * constructor taking a File parameter; this is the mechanism * for saving macros. * * @param file The file in which to save the macro. * @throws IOException If an error occurs while generating the XML for * the output file. * @see #saveToFile(String) */ public void saveToFile(File file) throws IOException { saveToFile(file.getAbsolutePath()); } /** * Saves this macro to a file. This file can later be read in by the * constructor taking a File parameter; this is the mechanism * for saving macros. * * @param fileName The name of the file in which to save the macro. * @throws IOException If an error occurs while generating the XML for * the output file. * @see #saveToFile(File) */ public void saveToFile(String fileName) throws IOException { /* * This method writes the XML document in the following format: * * * * test * abcdefg * [...] * ... * * */ try { DocumentBuilder db = DocumentBuilderFactory.newInstance(). newDocumentBuilder(); DOMImplementation impl = db.getDOMImplementation(); Document doc = impl.createDocument(null, ROOT_ELEMENT, null); Element rootElement = doc.getDocumentElement(); // Write the name of the macro. Element nameElement = doc.createElement(MACRO_NAME); nameElement.appendChild(doc.createCDATASection(name)); rootElement.appendChild(nameElement); // Write all actions (the meat) in the macro. for (MacroRecord record : macroRecords) { Element actionElement = doc.createElement(ACTION); actionElement.setAttribute(ID, record.id); if (record.actionCommand!=null && record.actionCommand.length()>0) { // Remove illegal characters. I'm no XML expert, but // I'm not sure what I'm doing wrong. If we don't // strip out chars with Unicode value < 32, our // generator will insert '&#', which will cause // our parser to barf when reading the macro back in // (it says "Invalid XML character"). But why doesn't // our generator tell us the character is invalid too? String command = record.actionCommand; for (int j=0; j




© 2015 - 2024 Weber Informatics LLC | Privacy Policy