Package bm.vm

Source Code of bm.vm.ScriptingClassLoader

package bm.vm;

/* -----------------------------------------------------------------------------
    bmScript Scripting language for Mobile Devices
    Copyright (C) 2004-2008 Elondra S.L.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.
    If not, see <a href="http://www.gnu.org/licenses">http://www.gnu.org/licenses</a>.
----------------------------------------------------------------------------- */

import bm.core.io.SerializationException;
import bm.core.io.SerializerInputStream;
import bm.vm.sys.Map;
import bm.vm.sys.ScriptingObject;
import bm.vm.sys.VMSystem;
import bm.vm.sys.Void;

import java.io.InputStream;
import java.util.Hashtable;
import java.util.Vector;
/*
* File Information
*
* Created on       : 07-oct-2007 1:06:45
* Created by       : narciso
* Last modified by : $Author$
* Last modified on : $Date$
* Revision         : $Revision$
*/

/**
* Loads classes from a certain storage.<br/>
* This default implementation can be substituted at runtime with other class
* loaders.<br/>
* This default implementation looks for classes as resources bundled with the
* .jar file, stored in a folder called <i>vm</i> located at the root directory
* of the .jar file.<br/>
* A subclass of this default loader MUST always default to this class load
* method when it can not find a class, as this class has also the ability of
* loading system classes.
*
* @author <a href="mailto:narciso@elondra.com">Narciso Cerezo</a>
* @version $Revision$
*/
public class ScriptingClassLoader
{
    /**
     * Some classes can be accessed both by name or by alias.
     */
    private Hashtable aliases;
    /**
     * System classes, like int, double, etc.
     */
    private Hashtable systemClasses;
    /**
     * Cache of already loaded real scripting clasess.
     */
    private Hashtable loadedClasses = new Hashtable( 10 );

    /**
     * Clasess that are loading now, for managing dependencies.
     */
    private Vector loading = new Vector( 10 );
    /**
     * Proxies for classes that are currently loading, to manage cyclic
     * dependencies of classes provinding proxies that are resolved when both
     * classes are fully loaded.
     */
    private Hashtable proxies = new Hashtable( 10 );

    /**
     * The virtual machine for this class loader.
     */
    protected VirtualMachine vm;

    /**
     * Create a new class loader for a virtual machine.
     *
     * @param vm virtual machine
     */
    public ScriptingClassLoader( final VirtualMachine vm )
    {
        this.vm = vm;
        systemClasses = new Hashtable( 9 );
        addSystemClass( "boolean" );
        addSystemClass( "String" );
        addSystemClass( "byte" );
        addSystemClass( "short" );
        addSystemClass( "int" );
        addSystemClass( "long" );
        addSystemClass( "double" );
        systemClasses.put( "map", new Map( vm ) );
        systemClasses.put( "system", new VMSystem( vm ) );
        systemClasses.put( "object", new ScriptingObject( vm ) );
        systemClasses.put( "void", new Void( vm ) );
        aliases = new Hashtable( 9 );
        aliases.put( "sys.Boolean", "boolean" );
        aliases.put( "sys.String", "String" );
        aliases.put( "sys.Byte", "byte" );
        aliases.put( "sys.Short", "short" );
        aliases.put( "sys.Integer", "int" );
        aliases.put( "sys.Long", "long" );
        aliases.put( "sys.Double", "double" );
        aliases.put( "sys.Map", "map" );
        aliases.put( "sys.System", "system" );
        aliases.put( "sys.Object", "object" );
        aliases.put( "sys.Void", "void" );
    }

    /**
     * Add a native class to the class loader.
     *
     * @param name class name
     * @param clazz class
     */
    public void addNativeClass( final String name, final NativeClass clazz )
    {
        systemClasses.put( name, clazz );
    }

    /**
     * Remove a native class from the class loader.
     *
     * @param name class name
     */
    public void removeNativeClass( final String name )
    {
        Object clazz = systemClasses.get( name );
        if( clazz != null && clazz instanceof NativeClass )
        {
            systemClasses.remove( name );
        }
    }

    /**
     * Get a class given its name or alias.
     *
     * @param name name or alias
     * @return class
     * @throws ScriptingClassNotFoundException if the class cant be found
     */
    public ScriptingClass load( final String name )
            throws ScriptingClassNotFoundException
    {
        if( aliases.containsKey( name ) )
        {
            return (ScriptingClass) systemClasses.get( aliases.get( name ) );
        }
        else if( systemClasses.containsKey( name ) )
        {
            return (ScriptingClass) systemClasses.get( name );
        }
        else if( loadedClasses.containsKey( name ) )
        {
            return (ScriptingClass) loadedClasses.get( name );
        }
        else
        {
            return loadNonSystemClass( name );
        }
    }

    /**
     * Get the alias for a given class name.
     *
     * @param name class name
     * @return alias or null if class has no alias
     */
    public String getAlias( final String name )
    {
        return (String) aliases.get( name );
    }

    /**
     * Load a class from an input stream.
     *
     * @param is input stream
     * @param name class name
     * @return the class
     * @throws SerializationException on errors loading class
     */
    protected ScriptingClass load( final InputStream is, final String name )
            throws SerializationException
    {
        startLoad( name );
        final SerializerInputStream in = new SerializerInputStream( is );
        final ScriptingClass clazz = new ScriptingClass( vm );
        clazz.deserialize( in );
        if( proxies.containsKey( name ) )
        {
            final ScriptingClassProxy proxy =
                    (ScriptingClassProxy) proxies.get( name );
            proxy.setClazz( clazz );
        }
        loadedClasses.put( name, clazz );
        endLoad( name );
        return clazz;
    }

    /**
     * Starts loading a class.
     *
     * @param name class name
     */
    protected void startLoad( final String name )
    {
        if( !loading.contains( name ) )
        {
            loading.addElement( name );
        }
    }

    /**
     * End loading a class. Removes also the proxy if it was needed.
     *
     * @param name class name
     */
    protected synchronized void endLoad( final String name )
    {
        loading.removeElement( name );
        if( proxies.containsKey( name ) )
        {
            proxies.remove( name );
        }
    }

    /**
     * Check if a class is loading.
     *
     * @param name class name
     * @return true if so
     */
    protected boolean isLoading( final String name )
    {
        return loading.contains( name );
    }

    /**
     * Get the virtual machine for this class loader.
     *
     * @return virtual machine
     */
    public VirtualMachine getVirtualMachine()
    {
        return vm;
    }

    private void addSystemClass( final String name )
    {
        final ScriptingClass sysClass = new ScriptingClass( vm );
        sysClass.name = name;
        sysClass.systemClass = true;
        sysClass.properties = new Hashtable( 1 );
        sysClass.properties.put( "value", sysClass );
        systemClasses.put( sysClass.name, sysClass );
    }

    private ScriptingClass loadNonSystemClass( final String name )
            throws ScriptingClassNotFoundException
    {
        if( isLoading( name ) )
        {
            ScriptingClassProxy proxy = (ScriptingClassProxy) proxies.get( name );
            if( proxy == null )
            {
                proxy = new ScriptingClassProxy( vm );
                proxies.put( name, proxy );
            }
            return proxy;
        }
        else
        {
            InputStream is = null;
            try
            {
                is = getClass().getResourceAsStream( "/vm/" + name + ".bmc" );
                if( is != null )
                {
                    return load( is, name );
                }
                else
                {
                    throw new ScriptingClassNotFoundException( 0, name );
                }
            }
            catch( Exception e )
            {
                throw new ScriptingClassNotFoundException( 0, name );
            }
            finally
            {
                if( is != null ) try{ is.close(); }catch( Exception e ){}
                endLoad( name );
            }
        }
    }

}
TOP

Related Classes of bm.vm.ScriptingClassLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.