String sourceName,
Class<?> expectedType)
throws IOException, ParseException
{
// Step 1. Find the name resolver
NameResolver resolver = XMLUtils.getResolver(context);
if (resolver == null)
{
if (_LOG.isWarning())
_LOG.warning("Internal error: couldn't find NameResolver");
return Collections.emptyList();
}
// Step 2. Find an InputStreamProvider. Mark a dependency on the base provider (if necessary)
InputStreamProvider importProvider = resolver.getProvider(sourceName);
InputStreamProvider baseProvider = XMLUtils.getInputStreamProvider(context);
if (baseProvider instanceof CachingInputStreamProvider)
{
// important: hasSourceChanged takes into account this dependency
((CachingInputStreamProvider)baseProvider).addCacheDependency(importProvider);
}
// Step 3. Detect if this will be a circular include
ArrayList<Object> list = (ArrayList<Object>) context.getProperty(_SHARE_NAMESPACE,
_INCLUDE_STACK);
Object identifier = importProvider.getIdentifier();
if ((list != null) && (list.contains(identifier)))
{
// Just logging an error isn't really enough - the include
// will fail, but parsing continues and you'll get a stack overflow. So, instead, we throw
// an exception...
throw new ParseException(_LOG.getMessage(
"CIRCULAR_INCLUDE_DETECTED", sourceName), 0);
}
// Step 4. Try to get a cached version
// =-=jmw I don't know when the cached returns non-null other than when
// the same import is included twice. This step (and Step 7) might not be worth it.
// comment out caching code
//Object cached = importProvider.getCachedResult();
//if ((cached != null) && expectedType.isInstance(cached))
//{
//return (List<List<SkinStyleSheetNode>>)cached;
//}
// Step 5. Set up the new context; first, clone the original
ParseContext newContext = (ParseContext)context.clone();
// Add the current identifier to the stack (used for detecting circular includes)
// placed on the ParseContext
// cloning ParseContext does a shallow copy. It doesn't copy this list.
if (list == null)
list = new ArrayList<Object>();
else
list = new ArrayList<Object>(list);
list.add(identifier);
newContext.setProperty(_SHARE_NAMESPACE, _INCLUDE_STACK, list);
InputStream stream = importProvider.openInputStream();
try
{
// Store a resolver relative to the file we're about to parse. This will be used for imports.
// Store the inputStreamProvider on the context;
// this will be used to get the document's timestamp later on
XMLUtils.setResolver(newContext, resolver.getResolver(sourceName));
XMLUtils.setInputStreamProvider(newContext, importProvider);
// PARSE!
// create a SkinStyleSheetNode
// (contains a namespaceMap and a List of SkinSelectorPropertiesNodes