Package org.fife.ui.rtextarea

Source Code of org.fife.ui.rtextarea.Macro

/*
* 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
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rtextarea;

import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;

import org.fife.io.UnicodeReader;


/**
* A macro as recorded/played back by an <code>RTextArea</code>.
*
* @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    = "<Untitled>";

  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 java.io.EOFException If an EOF is reached unexpectedly (i.e.,
   *         the file is corrupt).
   * @throws FileNotFoundException If the specified file does not exist, is
   *         a directory instead of a regular file, or otherwise cannot be
   *         opened.
   * @throws IOException If an I/O exception occurs while reading the file.
   * @see #saveToFile
   */
  public Macro(File file) throws EOFException, FileNotFoundException,
                IOException {

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = null;
    Document doc = null;
    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==false) {
      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());
      Iterator i = records.iterator();
      while (i.hasNext()) {
        MacroRecord record = (MacroRecord)i.next();
        macroRecords.add(record);
      }
    }
    else {
      macroRecords = new ArrayList(10);
    }
 
  }


  /**
   * Adds a macro record to this macro.
   *
   * @param record The record to add.  If <code>null</code>, 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.
   *
   * @return The macro's name.
   * @see #setName
   */
  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 node The root node of the parsed XML document.
   * @return <code>true</code> if the macro initialization went okay;
   *         <code>false</code> if an error occurred.
   */
  private boolean initializeFromXMLFile(Element root) {

    /*
     * This method expects the XML document to be in the following format:
     *
     * <?xml version="1.0" encoding="UTF-8" ?>
     * <macro>
     *    <macroName>test</macroName>
     *    <action id="default-typed">abcdefg</action>
     *    [<action id=...>...</action>]
     *    ...
     * </macro>
     *
     */

    NodeList childNodes = root.getChildNodes();
    int count = childNodes.getLength();

    for (int i=0; i<count; i++) {

      Node node = childNodes.item(i);
      int type = node.getNodeType();
      switch (type) {

        // Handle element nodes.
        case Node.ELEMENT_NODE:

          String nodeName = node.getNodeName();

          if (nodeName.equals(MACRO_NAME)) {
            NodeList childNodes2 = node.getChildNodes();
            name = UNTITLED_MACRO_NAME;
            if (childNodes2.getLength()>0) {
              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 a text file.  This file can later be read in by
   * the constructor taking a <code>File</code> 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.
   */
  public void saveToFile(String fileName) throws IOException {

    /*
     * This method writes the XML document in the following format:
     *
     * <?xml version="1.0" encoding="UTF-8" ?>
     * <macro>
     *    <macroName>test</macroName>
     *    <action id="default-typed">abcdefg</action>
     *    [<action id=...>...</action>]
     *    ...
     * </macro>
     *
     */

    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);
      rootElement.appendChild(nameElement);

      // Write all actions (the meat) in the macro.
      int numActions = macroRecords.size();
      for (int i=0; i<numActions; i++) {
        MacroRecord record = (MacroRecord)macroRecords.get(i);
        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 '&#<value>', 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<command.length(); j++) {
            if (command.charAt(j)<32) {
              command = command.substring(0,j);
              if (j<command.length()-1)
                command += command.substring(j+1);
            }
          }
          Node n = doc.createCDATASection(command);
          actionElement.appendChild(n);
        }
        rootElement.appendChild(actionElement);
      }

      // Dump the XML out to the file.
      StreamResult result = new StreamResult(new File(fileName));
      DOMSource source = new DOMSource(doc);
      TransformerFactory transFac = TransformerFactory.newInstance();
      Transformer transformer = transFac.newTransformer();
      transformer.setOutputProperty(OutputKeys.INDENT, "yes");
      transformer.setOutputProperty(OutputKeys.ENCODING, FILE_ENCODING);
      transformer.transform(source, result);

    } catch (RuntimeException re) {
      throw re; // Keep FindBugs happy.
    } catch (Exception e) {
      throw new IOException("Error generating XML!");
    }

  }


  /**
   * Sets the name of this macro.
   *
   * @param name The new name for the macro.
   * @see #getName
   */
  public void setName(String name) {
    this.name = name;
  }


  /**
   * A "record" of a macro is a single action in the macro (corresponding to
   * a key type and some action in the editor, such as a letter inserted into
   * the document, scrolling one page down, selecting the current line,
   * etc.).
   */
  static class MacroRecord {

    public String id;
    public String actionCommand;

    public MacroRecord() {
      this(null, null);
    }

    public MacroRecord(String id, String actionCommand) {
      this.id = id;
      this.actionCommand = actionCommand;
    }

  }


}
TOP

Related Classes of org.fife.ui.rtextarea.Macro

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.