/******************************************************************************
* Copyright (c) 2016 Oracle
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Konstantin Komissarchik - initial implementation and ongoing maintenance
******************************************************************************/
package org.eclipse.sapphire;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* An abstraction for finding classes and other resources typically loaded from a class loader.
* This abstraction allows integration with systems that do not provide easy access to a class loader.
*
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/
public abstract class Context
{
/**
* Returns a context based on the specified class loader.
*
* @param loader the class loader that should be used as the context basis
* @return a context based on the specified class loader
*/
public static final Context adapt( final ClassLoader loader )
{
if( loader == null )
{
throw new IllegalArgumentException();
}
return new ClassLoaderContext( loader );
}
/**
* Returns a context based on the specified class. Such a context behaves similarly to
* a context built around a class loader. The difference is ability to find classes and
* resources based on simple (unqualified) names as long as they are located in the package
* of the specified class. The package-level search is performed only if standard class loader
* search fails to find a class or a resource.
*
* @param cl the class that should be used as the context basis
* @return a context based on the specified class
*/
public static final Context adapt( final Class<?> cl )
{
if( cl == null )
{
throw new IllegalArgumentException();
}
return new ClassContext( cl );
}
/**
* Finds a class with specified name.
*
* @param name the name of the class
* @return a class with specified name or null if not found
* @throws IllegalArgumentException if class name is null
*/
public abstract <T> Class<T> findClass( String name );
/**
* Finds a resource with specified name.
*
* @param name the name of the resource
* @return a resource with specified name or null if not found
* @throws IllegalArgumentException if resource name is null
*/
public abstract InputStream findResource( String name );
/**
* Implementation of Context based on a class loader.
*/
private static class ClassLoaderContext extends Context
{
private final ClassLoader loader;
public ClassLoaderContext( final ClassLoader loader )
{
if( loader == null )
{
throw new IllegalArgumentException();
}
this.loader = loader;
}
@Override
@SuppressWarnings( "unchecked" )
public <T> Class<T> findClass( final String name )
{
if( name == null )
{
throw new IllegalArgumentException();
}
try
{
return (Class<T>) this.loader.loadClass( name );
}
catch( ClassNotFoundException e )
{
// Intentionally converting ClassNotFoundException to null return.
}
return null;
}
@Override
public InputStream findResource( final String name )
{
if( name == null )
{
throw new IllegalArgumentException();
}
final URL url = this.loader.getResource( name );
if( url != null )
{
try
{
return url.openStream();
}
catch( IOException e )
{
// Failure to open is equated with not found by returning null.
}
}
return null;
}
@Override
public boolean equals( final Object obj )
{
if( obj instanceof ClassLoaderContext )
{
final ClassLoaderContext context = (ClassLoaderContext) obj;
return ( this.loader == context.loader );
}
return false;
}
@Override
public int hashCode()
{
return this.loader.hashCode();
}
}
/**
* Implementation of Context based on a class.
*/
private static final class ClassContext extends ClassLoaderContext
{
private final String pkg;
private final String path;
public ClassContext( final Class<?> cl )
{
super( cl.getClassLoader() );
final Package pkg = cl.getPackage();
if( pkg == null )
{
this.pkg = null;
this.path = null;
}
else
{
this.pkg = pkg.getName();
this.path = this.pkg.replace( '.', '/' );
}
}
@Override
public <T> Class<T> findClass( final String name )
{
Class<T> cl = super.findClass( name );
if( cl == null && name.indexOf( '.' ) == -1 && this.pkg != null)
{
cl = super.findClass( this.pkg + "." + name );
}
return cl;
}
@Override
public InputStream findResource( final String name )
{
InputStream stream = super.findResource( name );
if( stream == null && name.indexOf( '/' ) == -1 && this.path != null )
{
stream = super.findResource( this.path + "/" + name );
}
return stream;
}
@Override
public boolean equals( final Object obj )
{
if( obj instanceof ClassContext )
{
final ClassContext context = (ClassContext) obj;
return super.equals( context ) && this.pkg.equals( context.pkg );
}
return false;
}
@Override
public int hashCode()
{
return super.hashCode() ^ this.pkg.hashCode();
}
}
}