/*
* JBoss, Home of Professional Open Source
*
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.jbpm.pvm.internal.xml;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.ListIterator;
import java.util.Map;
import java.util.Stack;
import javax.xml.parsers.DocumentBuilder;
import org.jbpm.pvm.internal.stream.FileStreamInput;
import org.jbpm.pvm.internal.stream.InputStreamInput;
import org.jbpm.pvm.internal.stream.ResourceStreamInput;
import org.jbpm.pvm.internal.stream.StreamInput;
import org.jbpm.pvm.internal.stream.StringStreamInput;
import org.jbpm.pvm.internal.stream.UrlStreamInput;
import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
/** information related to one single parse operation, for instructions
* see {@link Parser}.
*
* @author Tom Baeyens
*/
public class Parse extends ProblemList implements ErrorHandler {
private static final long serialVersionUID = 1L;
public static final String CONTEXT_KEY_DEPLOYMENT = "deployment";
public static final String CONTEXT_KEY_PROCESS_LANGUAGE_ID = "proclangid";
public static final String CONTEXT_KEY_BINDINGS = "bindings";
public static final String CONTEXT_KEY_MIGRATIONS = "migrations";
protected Parser parser;
protected StreamInput streamInput;
protected InputStream inputStream;
protected InputSource inputSource;
protected DocumentBuilder documentBuilder = null;
protected Document document = null;
protected Map<String, Object> contextMap;
protected Stack<Object> contextStack;
protected Object documentObject;
protected Parse(Parser parser) {
this.parser = parser;
}
// specifying the input source //////////////////////////////////////////////
/** specify an input stream as the source for this parse */
public Parse setInputStream(InputStream inputStream) {
this.streamInput = new InputStreamInput(inputStream);
return this;
}
/** specify a URL as the source for this parse */
public Parse setUrl(URL url) {
this.streamInput = new UrlStreamInput(url);
return this;
}
/** specify a file as the source for this parse */
public Parse setFile(File file) {
this.streamInput = new FileStreamInput(file);
return this;
}
/** specify a resource as the source for this parse */
public Parse setResource(String resource) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
this.streamInput = new ResourceStreamInput(resource, classLoader);
return this;
}
/** specify an XML string as the source for this parse */
public Parse setString(String xmlString) {
this.streamInput = new StringStreamInput(xmlString);
return this;
}
/** specify a {@link StreamInput} as the source for this parse */
public Parse setStreamSource(StreamInput streamInput) {
this.streamInput = streamInput;
return this;
}
/** specify an InputStream as the source for this parse */
public Parse setInputSource(InputSource inputSource) {
this.inputSource = inputSource;
return this;
}
/** normally the Document Object Model is created during the
* parse execution, but providing a document can be convenient when
* the DOM is already available and only the walking of the
* DOM needs to be done by the parser. If the document
* is provide, building the DOM from a source is skipped. */
public Parse setDocument(Document document) {
this.document = document;
return this;
}
/** provides the result of this parse operation. */
public Parse setDocumentObject(Object object) {
this.documentObject = object;
return this;
}
// retrieving input source //////////////////////////////////////////////////
protected InputSource getInputSource() {
if (inputSource!=null) {
return inputSource;
}
if (streamInput!=null) {
inputStream = streamInput.openStream();
return new InputSource(inputStream);
}
addProblem("no source specified to parse");
return null;
}
// parse execution //////////////////////////////////////////////////////////
/** perform the actual parse operation with the specified input source. */
public Parse execute() {
parser.execute(this);
return this;
}
// problems /////////////////////////////////////////////////////////////////
/** part of {@link ErrorHandler} to capture XML parsing problems. */
public void error(SAXParseException e) {
addXmlValidationProblem(e, ProblemImpl.TYPE_XML_VALIDATION_ERROR);
}
/** part of {@link ErrorHandler} to capture XML parsing problems. */
public void fatalError(SAXParseException e) {
addXmlValidationProblem(e, ProblemImpl.TYPE_XML_VALIDATION_ERROR);
}
/** part of {@link ErrorHandler} to capture XML parsing problems. */
public void warning(SAXParseException e) {
addXmlValidationProblem(e, ProblemImpl.TYPE_XML_VALIDATION_WARNING);
}
protected void addXmlValidationProblem(SAXParseException e, String type) {
ProblemImpl problem = new ProblemImpl(e.getMessage(), e, type);
problem.setLine(e.getLineNumber());
problem.setColumn(e.getColumnNumber());
addProblem(problem);
}
/** throws an exception with appropriate message in case the parse contains
* errors or fatal errors. This method also logs the problems with severity
* 'warning'. */
public Parse checkErrors(String description) {
if (hasErrors()) {
throw getJbpmException();
}
return this;
}
// context map //////////////////////////////////////////////////////////////
public Parse contextMapPut(String key, Object value) {
if (contextMap==null) {
contextMap = new HashMap<String, Object>();
}
contextMap.put(key, value);
return this;
}
public Object contextMapGet(String key) {
if (contextMap!=null) {
return contextMap.get(key);
}
return null;
}
public Parse contextMapRemove(String key) {
if (contextMap!=null) {
contextMap.remove(key);
}
return this;
}
public Parse propagateContexMap(Parse parse) {
if (parse.contextMap==null) {
parse.contextMap = new HashMap<String, Object>();
}
if (this.contextMap!=null) {
parse.contextMap.putAll(this.contextMap);
}
this.contextMap = parse.contextMap;
return this;
}
// contex stack /////////////////////////////////////////////////////////////
/** push a contextual object on the stack of this parse. */
public Parse contextStackPush(Object object) {
if (contextStack==null) {
contextStack = new Stack<Object>();
}
contextStack.push(object);
return this;
}
/** remove a contextual object from the stack. */
public Object contextStackPop() {
if (contextStack!=null) {
return contextStack.pop();
}
return null;
}
/** look up the top contextual object from the stack. */
public Object contextStackPeek() {
if (contextStack!=null) {
return contextStack.peek();
}
return null;
}
/** search a contextual object in the stack by type. */
public <T> T contextStackFind(Class<T> clazz) {
if ( (contextStack!=null)
&& (! contextStack.isEmpty())
) {
ListIterator<Object> listIter = contextStack.listIterator(contextStack.size());
while (listIter.hasPrevious()) {
Object object = listIter.previous();
if (clazz.isInstance(object)) {
return clazz.cast(object);
}
}
}
return null;
}
// getters //////////////////////////////////////////////////////////////////
/** the result of this parse operation. */
public Object getDocumentObject() {
return documentObject;
}
/** the Document Object Model (DOM). */
public Document getDocument() {
return document;
}
}