/* * * Copyright 2007-2008 University Of Southern California * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package edu.isi.pegasus.planner.parser; import edu.isi.pegasus.common.logging.LogManager; import edu.isi.pegasus.planner.classes.PegasusBag; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Stack; import org.xml.sax.Attributes; import org.xml.sax.SAXException; /** * An abstract base class that XML Parsers can use if they use stack internally * to store the elements encountered while parsing XML documents using SAX * * @author Karan Vahi vahi@isi.edu * @version $Revision$ */ public abstract class StackBasedXMLParser extends Parser { /** * Count the depths of elements in the document */ protected int mDepth; /** * The stack of objects kept around. */ protected Stack mStack; /** * A boolean indicating that parsing is done. */ protected boolean mParsingDone; /** * A set of containing the unsupported element attributes */ protected Set<String> mUnsupportedElementAttributes; /** * The default Constructor. * * */ /* public StackBasedXMLParser( ) { this( PegasusProperties.nonSingletonInstance() ); }*/ /** * The overloaded constructor. * * @param bag the <code>PegasusBag</code> to be used. */ public StackBasedXMLParser( PegasusBag bag ) { super( bag ); mStack = new Stack(); mDepth = 0; mUnsupportedElementAttributes = new HashSet(); } /** * Sets the list of external real locations where the XML schema may be found. * Since this list can be determined at run-time through properties etc., we * expect this function to be called between instantiating the parser, and * using the parser * */ public void setSchemaLocations( ){ //setting the schema Locations String schemaLoc = getSchemaLocation(); mLogger.log( "Picking schema " + schemaLoc, LogManager.CONFIG_MESSAGE_LEVEL); String list = getSchemaNamespace( ) + " " + schemaLoc; setSchemaLocations( list ); } /** * Composes the <code>SiteData</code> object corresponding to the element * name in the XML document. * * @param element the element name encountered while parsing. * @param names is a list of attribute names, as strings. * @param values is a list of attribute values, to match the key list. * * @return the relevant SiteData object, else null if unable to construct. * * @exception IllegalArgumentException if the element name is too short. */ public abstract Object createObject( String element, List names, List values ); /** * This method sets the relations between the currently finished XML * element(child) and its containing element in terms of Java objects. * Usually it involves adding the object to the parent's child object * list. * * @param childElement name is the the child element name * @param parent is a reference to the parent's Java object * @param child is the completed child object to connect to the parent * * @return true if the element was added successfully, false, if the * child does not match into the parent. */ public abstract boolean setElementRelation( String childElement, Object parent, Object child ); /** * */ public void endDocument() { mParsingDone = true; } /** * This method defines the action to take when the parser begins to parse * an element. * * @param namespaceURI is the URI of the namespace for the element * @param localName is the element name without namespace * @param qName is the element name as it appears in the docment * @param atts has the names and values of all the attributes */ public void startElement( String namespaceURI, String localName, String qName, Attributes atts ) throws SAXException{ //one more element level mDepth++; List names = new java.util.ArrayList(); List values = new java.util.ArrayList(); for ( int i=0; i < atts.getLength(); ++i ) { String name = atts.getLocalName(i) ; String value = atts.getValue(i) ; names.add(name); values.add(value); } //System.out.println( "QNAME " + qName + " NAME " + names + "\t Values" + values ); Object object = createObject( qName, names, values ); if ( object != null ){ mStack.push( new ParserStackElement( qName, object ) ); } else{ mLogger.log( "Unknown element in xml :" + namespaceURI + ":" + localName + ":" + qName, LogManager.ERROR_MESSAGE_LEVEL ); throw new SAXException( "Unknown or Empty element while parsing " ); } } /** * The parser is at the end of an element. Triggers the association of * the child elements with the appropriate parent elements. * * @param namespaceURI is the URI of the namespace for the element * @param localName is the element name without namespace * @param qName is the element name as it appears in the docment */ public void endElement( String namespaceURI, String localName, String qName ) throws SAXException{ // that's it for this level mDepth--; mLogger.log( "</" + localName + "> at " + this.mLocator.getLineNumber() + ":" + mLocator.getColumnNumber() , LogManager.TRACE_MESSAGE_LEVEL ); ParserStackElement tos = ( ParserStackElement ) mStack.pop(); if ( ! qName.equals( tos.getElementName() ) ) { String error = "Top of Stack " + tos.getElementName() + " does not mactch " + qName; mLogger.log( error, LogManager.FATAL_MESSAGE_LEVEL ); throw new SAXException( error ); } // add pieces to lower levels ParserStackElement peek = mStack.empty() ? null : (ParserStackElement) mStack.peek(); if (!setElementRelation( tos.getElementName(), peek == null ? null : peek.getElementObject(), tos.getElementObject())) { String element = peek == null ? "root-element" : peek.getElementName(); mLogger.log( "Element " + tos.getElementName() + " does not fit into element " + element, LogManager.ERROR_MESSAGE_LEVEL ); } //reinitialize our cdata handler at end of each element mTextContent.setLength( 0 ); } /** * * @param element * @param attribute * @param value */ public void log( String element, String attribute, String value) { //to be enabled when logging per queue. mLogger.log( "For element " + element + " found " + attribute + " -> " + value, LogManager.TRACE_MESSAGE_LEVEL ); } /** * This is called when an attribute is encountered for an element that is invalid * from the schema context and is not supported. * * @param element the element name * @param attribute the attribute name * @param value the attribute value */ public void complain(String element, String attribute, String value) { mLogger.log( "For element " + element + " invalid attribute found " + attribute + " -> " + value, LogManager.ERROR_MESSAGE_LEVEL ); } /** * This is called when an attribute is encountered for an element that is valid * in the schema context but not supported right now. * * @param element the element name * @param attribute the attribute name * @param value the attribute value */ public void attributeNotSupported(String element, String attribute, String value) { StringBuffer sb = new StringBuffer(); sb.append( "element" ).append( ":" ).append( "attribute" ).append( "->" ).append( value ); String key = sb.toString(); if( !this.mUnsupportedElementAttributes.contains(key) ){ mLogger.log( "For element " + element + " attribute currently not supported " + attribute + " -> " + value, LogManager.WARNING_MESSAGE_LEVEL ); this.mUnsupportedElementAttributes.add( key ); } } /** * Called when certain element nesting is allowed in the XML schema * but is not supported in the code as yet. * * @param parent parent element * @param child child element */ public void unSupportedNestingOfElements(String parent, String child ) { StringBuffer sb = new StringBuffer(); sb.append( "Unsupported nesting for element " ).append( child ). append( " in parent element " ).append( parent ); mLogger.log( sb.toString(), LogManager.WARNING_MESSAGE_LEVEL ); } }