[java]
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.DefaultHandler2;
import org.xml.sax.helpers.XMLReaderFactory;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetLoader;
/****************************************************************************
- Smart Multi-Purpose XMLLoader for JM3s AssetManger, providing an easy and
- robust way to import generic XML-data.
-
@author .rhavin grobert
-
@version 0.4.1 */
public abstract class XMLLoader extends DefaultHandler2
implements AssetLoader
{
//
// members
/** The Object we want to create */
private Object object = null;
/** The stack of currently processed tags */
protected ArrayList<String> TagStack = new ArrayList<String>();
/** The AssetInfo given by the AssetManager */
protected AssetInfo assetInfo = null;
/** The current skip-level */
private int skiplevel = 0;
/** The path we're loading from */
private String strPath = "";
//
// Public methods
/** get the currently processed tag */
public String tagCurrent()
{
int iLast = TagStack.size() - 1;
if (iLast < 0)
return "";
return TagStack.get(iLast);
}
/** Get object */
public Object getObject()
{return object;}
/** Set object directly */
public void setObject(Object o)
{object = o;}
/** Shortcut to the currently loaded files path */
public String getPath()
{return strPath;}
/** Shortcut to load another asset */
public Object loadAsset(String strAsset)
{
if (assetInfo == null)
return null;
return assetInfo.getManager().loadAsset(strAsset);
}
//
// To-Override abstract methods
/*********************************************************************
* This method is called when the documents processing starts and
* should construct or otherwise set-up the object that the loader
* should return. You *MAY* return null and decide what object to
* construct later, for example, when first (root) tag is opened, by
* calling setObject().
* @return The object you want to fill from xml-file. */
protected abstract Object XMLbegin();
/*********************************************************************
* This method is called on every starting tag.
* @param uri The namespace URI, as specified by xmlns-attribute.
* @param localName The local name string.
* @param qName The name of the tag, this is usually what you're
* looking for.
* @param attribs The tags attribute reference.
* @return Return true if you want to parse this tag and its childs
* or false if this tag and all its content should be skipped. You'll
* still get an end-method call for this tag. */
protected abstract boolean XMLstart(String uri, String localName,
String qName, Attributes attribs);
/*********************************************************************
* This method is called each time a tag is closed.
* It is also called for empty tags.
* @param uri The namespace URI, as specified by xmlns-attribute.
* @param localName The local name string.
* @param qName The name of the tag, this is usually what you're
* looking for. */
protected abstract void XMLend(String uri, String name, String qName);
/*********************************************************************
* This method is called at the documents end. Do final processing
* in this method. */
protected abstract void XMLfinal();
/*********************************************************************
* This method is called for all non-markup. Due to formating of the
* XML-file, it may sometimes completely consist of white-spaces.
* @param string The String consisting of non-markup. */
protected abstract void XMLchars(String string);
//
// Methods implementing loaders functionality
/*********************************************************************
* Load asset from the given input stream, parsing it into an
* app-usable object. Specified by JM3 interface AssetLoader */
@Override
public Object load(AssetInfo i) throws IOException
{
assetInfo = i;
strPath = getFolder(i.getKey().getName(), true);
XMLReader xr;
try
{
xr = XMLReaderFactory.createXMLReader();
xr.setContentHandler(this);
xr.setErrorHandler(this);
InputStreamReader r = new InputStreamReader(i.openStream());
xr.parse(new InputSource(r));
r.close();
return object;
}
catch (SAXException e)
{throw new IOException(i.getKey()+ " : " + e.toString());}
}
/*********************************************************************
* Handler called by the SAX2 event management at document start.
* We initialize the loaders return-object here. */
@Override
public void startDocument()
{
// let derived class construct the object.
object = XMLbegin();
}
/*********************************************************************
* Handler called by the SAX2 event management on every starting tag
* @param uri The namespace URI, as specified by xmlns-attribute.
* @param localName The local name (without prefix), or the empty
* string if namespace processing is not being performed.
* @param qName The qualified name (with prefix) of the tag, this is
* usually what you're looking for.
* @param attribs The tags attribute reference.
* @throws SAXException if something wired happens. */
@Override
public void startElement(String uri, String localName, String qName,
Attributes attribs) throws SAXException
{
// in skip-mode, we just follow structure until end-tag
if (skiplevel != 0)
{
skiplevel++;
return;
}
// add element to processing stack
TagStack.add(qName);
// let derived class handle element
if (XMLstart(uri, localName, qName, attribs))
return;
// we should skip this tag, set skip-level
skiplevel = 1;
}
/*********************************************************************
* Handler called by the SAX2 event management each time a tag is
* closed. It is also called for empty tags.
* @param uri The namespace URI, as specified by xmlns-attribute.
* @param localName The local name (without prefix), or the empty
* string if namespace processing is not being performed.
* @param qName The qualified name (with prefix) of the tag, this is
* usually what you're looking for. */
@Override
public void endElement(String uri, String name, String qName)
{
// in skip-mode, we just follow structure until end-tag
if (skiplevel != 0)
{
if (skiplevel-->1)
return;
// end-tag of skipped tag reached, leave skip-mode
skiplevel = 0;
}
// let derived class handle element
XMLend(uri, name, qName);
// remove element from processing stack
int iLast = TagStack.size() - 1;
if (iLast >= 0)
TagStack.remove(iLast);
}
/*********************************************************************
* Handler called by the SAX2 event management for all non-markup-
* events. Due to formating of the XML-file, it may sometimes
* completely consist of white-spaces.
* @param ch - The characters.
* @param start - The start position in the character array.
* @param length - Number of chars to use from the array.*/
@Override
public void characters(char ch[], int start, int length)
{
if (skiplevel != 0)
return;
XMLchars(String.copyValueOf(ch, start, length));
}
/*********************************************************************
* Handler called by the SAX2 event management at the end of the
* parsed document. */
@Override
public void endDocument() throws SAXException
{
// let derived class do final processing
XMLfinal();
}
/*********************************************************************
* As JM3 only uses slashes we provide this function to have a system
* independent way of getting folder paths.
* @param file The full path and name of the file.
* @param convert Set to true if you want the path to only use slashes
* as separator.
* @return The Path to the folder. */
public static String getFolder(String file, boolean convert)
{
String strFolder = "";
if (convert)
file = file.replace(File.separatorChar, '/');
// standard behavior
int idx = file.lastIndexOf('/');
if (idx > 0 && idx != file.length() - 1)
strFolder = file.substring(0, idx+1);
if (!convert || strFolder.length() > 0
|| File.separatorChar == '/')
return strFolder;
// check for system dependent separator char
idx = file.lastIndexOf(File.separatorChar);
if (idx > 0 && idx != file.length() - 1)
strFolder = file.substring(0, idx+1);
return strFolder;
}
[/java]