/*
* This file is part of muCommander, http://www.mucommander.com
* Copyright (C) 2002-2016 Maxence Bernard
*
* muCommander 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; either version 3 of the License, or
* (at your option) any later version.
*
* muCommander is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.mucommander.command;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
/**
* Class used to parse custom commands XML files.
* <p>
* Command file parsing is done through the {@link #read(InputStream,CommandBuilder) read} method, which is
* the only way to interact with this class.
* </p>
* <p>
* Note that while this class knows how to read the content of an command XML file, its role is not to interpret it. This
* is done by instances of {@link CommandBuilder}.
* </p>
* @see CommandsXmlConstants
* @see CommandBuilder
* @see CommandWriter
* @author Nicolas Rinaudo
*/
public class CommandReader extends DefaultHandler implements CommandsXmlConstants {
// - Instance variables --------------------------------------------------
// -----------------------------------------------------------------------
/** Where to send building messages. */
private CommandBuilder builder;
// - Initialisation ------------------------------------------------------
// -----------------------------------------------------------------------
/**
* Creates a new command reader.
* @param b where to send custom command events.
*/
private CommandReader(CommandBuilder b) {builder = b;}
// - XML interaction -----------------------------------------------------
// -----------------------------------------------------------------------
/**
* Parses the content of the specified input stream.
* <p>
* This method will go through the specified input stream and notify the builder of any new command declaration it
* encounters. Note that parsing is done in a very lenient fashion, and perfectly invalid XML files might not raise
* an exception. This is not a flaw in the parser, and both allows muCommander to be error resilient and the commands
* file format to be extended without having to rewrite most of this code.
* </p>
* <p>
* Note that even if an error occurs, both of the builder's {@link CommandBuilder#startBuilding()} and
* {@link CommandBuilder#endBuilding()} methods will still be called. Parsing will stop at the first error
* however, so while the builder is guaranteed to receive correct messages, it might not receive all declared
* commands.
* </p>
* @param in where to read command data from.
* @param b where to send building events to.
* @throws Exception thrown if any error occurs.
*/
public static void read(InputStream in, CommandBuilder b) throws CommandException, IOException {
b.startBuilding();
try {SAXParserFactory.newInstance().newSAXParser().parse(in, new CommandReader(b));}
catch(ParserConfigurationException e) {throw new CommandException(e);}
catch(SAXException e) {throw new CommandException(e);}
finally {b.endBuilding();}
}
// - XML methods ---------------------------------------------------------
// -----------------------------------------------------------------------
/**
* This method is public as an implementation side effect and should not be called directly.
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
// New custom command declaration.
if(qName.equals(ELEMENT_COMMAND)) {
String alias = attributes.getValue(ATTRIBUTE_ALIAS);
String command = attributes.getValue(ATTRIBUTE_VALUE);
// Makes sure the required attributes are there.
if(alias != null && command != null) {
CommandType type = CommandType.parseCommandType(attributes.getValue(ATTRIBUTE_TYPE));
String display = attributes.getValue(ATTRIBUTE_DISPLAY);
// Creates the command and passes it to the builder.
try {builder.addCommand(new Command(alias, command, type, display));}
catch(CommandException e) {throw new SAXException(e);}
}
}
}
}