/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.security.authorization.testwikis.internal.parser;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import org.xwiki.model.reference.EntityReferenceResolver;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.security.authorization.testwikis.TestDefinition;
import org.xwiki.security.authorization.testwikis.TestDefinitionParser;
/**
* Parser for parsing Tests Wikis XML definition.
*
* @version $Id: 29cc5b976154fa98c0b50712211d33ae9114631d $
* @since 5.0M2
*/
public class DefaultTestDefinitionParser extends DefaultHandler implements ElementParser, TestDefinitionParser
{
/**
* Stack of entity factories.
* The top level represent the active factories. The key is the element name supported by the factory.
* The same factory may be registered for different element names. Only one factory can be registered
* per element, the last arrived win.
*/
private Stack<Map<String, EntityFactory>> elementHandlers;
/** The locator to retrieve the current parsing location when logging errors. */
private Locator locator;
/** During a given parsing, the factory handling the root level. */
private TestWikisFactory handler;
/** During a given parsing, the entity resolver that could be used by factories. */
private EntityReferenceResolver<String> resolver;
/** During a given parsing, the entity serializer that could be used by factories. */
private EntityReferenceSerializer<String> serializer;
/** Default constructor. */
public DefaultTestDefinitionParser()
{
}
@Override
public TestDefinition parse(String filename,
EntityReferenceResolver<String> resolver,
EntityReferenceSerializer<String> serializer)
throws IOException, URISyntaxException, ParserConfigurationException, SAXException
{
URL url = ClassLoader.getSystemResource(filename);
if (url == null) {
throw new FileNotFoundException(filename);
}
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
SAXParser parser = parserFactory.newSAXParser();
this.resolver = resolver;
this.serializer = serializer;
elementHandlers = new Stack<Map<String, EntityFactory>>();
elementHandlers.push(new HashMap<String, EntityFactory>());
handler = new TestWikisFactory();
register(handler);
parser.parse(url.toURI().toString(), this);
if (elementHandlers.size() > 1) {
throw new SAXException("Handlers stack as been corrupted while parsing.");
}
elementHandlers = null;
locator = null;
this.resolver = null;
this.serializer = null;
TestDefinition wikis = getWikis();
handler = null;
return wikis;
}
@Override
public TestDefinition getWikis()
{
if (handler == null) {
throw new IllegalStateException("Wikis are only reachable during parsing");
}
return handler.getWikis();
}
@Override
public EntityReferenceResolver<String> getResolver()
{
return resolver;
}
@Override
public EntityReferenceSerializer<String> getSerializer()
{
return serializer;
}
@Override
public void register(EntityFactory handler) {
if (handler == null) {
return;
}
Map<String, EntityFactory> handlers = elementHandlers.peek();
for (String tagName : handler.getTagNames()) {
handlers.put(tagName, handler);
}
}
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException
{
Map<String, EntityFactory> handlers = elementHandlers.peek();
elementHandlers.push(new HashMap<String, EntityFactory>());
EntityFactory factory = handlers.get(name);
if (factory == null) {
throw new SAXException(getLocatedMessage("No factory for element %s", name));
}
factory.newElement(this, name, attributes);
}
@Override
public void endElement(String uri, String localName, String name) throws SAXException
{
elementHandlers.pop();
}
@Override
public void setDocumentLocator(Locator locator)
{
super.setDocumentLocator(locator);
this.locator = locator;
}
/**
* @return the current parsing location in string format " (at line %d, column %d)".
*/
private String getCurrentLocation()
{
if (locator != null) {
return String.format(" (at line %d, column %d)", locator.getLineNumber(), locator.getColumnNumber());
} else {
return " (Unknown location)";
}
}
@Override
public String getLocatedMessage(String format, Object... objects)
{
return String.format(format, objects) + getCurrentLocation();
}
@Override
public void warning(SAXParseException e) throws SAXException
{
throw new SAXException(e.getLocalizedMessage(), e);
}
@Override
public void fatalError(SAXParseException e) throws SAXException
{
throw new SAXException(e.getLocalizedMessage(), e);
}
@Override
public void error(SAXParseException e) throws SAXException
{
throw new SAXException(e.getLocalizedMessage(), e);
}
}