//*****************************************************************************
/*
** FILE:   xv_ViewFactory.java
**
** (c) 1998 Steve Withall.
**
** HISTORY:
**    25Jul98  stevew  Created, based on xc_CustomizerFactory.
*/
package xv;

import xm.xm_Factory;
import xm.xm_NodeTypeDefn;
import xm.xm_NodeTypeRegistry;

import xg.xg_Node;

import eh.eh_Debug;

import com.sun.java.swing.text.Element;
import com.sun.java.swing.text.View;
import com.sun.java.swing.text.ViewFactory;
import com.sun.java.swing.text.WrappedPlainView;

import java.lang.reflect.Constructor;

import java.lang.Class;

//*****************************************************************************
/** <p>Factory for generating views for displaying XML nodes in a JEditorPane.
 *  Since the constructors of swing.text.View objects must be passed the
 *  Element of which they are Views, this class can only create Views given
 *  an actual Element object: a node type and/or name are not sufficient.
 *  Consequently this class has a more limited range of methods than other
 *  classes driven by the node type registry (eg. xm_NodeFactory).</p>
 *
 *  <p>Note that this factory can only create View objects which have a single-
 *  argument constructor, where that single argument is the element for which it
 *  is a View. This is a little irritating, since there are Views (such as
 *  BoxView) which take additional arguments, but is not a major problem in
 *  practice. This limitation could be lifted with a considerable amount of
 *  work - the bulk of which is in configuring the argument values to use and
 *  implementing a mechanism to make them accessible to this factory. One
 *  possibility is to use an XML configuration file, and pass the list of
 *  attributes associated with one element type to the View class for it to
 *  use as it wishes.</p>
 *
 *  <p>Another possible enhancement would be to use an XML configuration file
 *  (an extension to an XSL stylesheet?) to offer more flexibility than simply
 *  specifying a single View class for each registry entry. (This argument
 *  applies equally well to customizer classes.)
 *
 *  <p>Given the name/type of an XML node (or a node itself), it creates a view
 *  object suitable for displaying it.</p>
 */
public class xv_ViewFactory extends xm_Factory implements ViewFactory
{
    /** Name of class from which all view objects must be derived. */
    public final static String UsualViewClassName = "com.sun.java.swing.text.View";

    /** Name of class which is passed as the first argument to the constructors of
     *  View objects. */
    public final static String ElementClassName   = "com.sun.java.swing.text.Element";

    /** Identities of class which is passed as the first argument to the
     *  constructors of View objects. */
    protected Class[]   ViewConstructorClasses   = new Class[1];

    /** Identities of class which is passed as the first argument to the
     *  constructors of View objects. */
    protected Object[]  ViewConstructorArgs      = new Object[1];

    //*****************************************************************************
    /** Default constructor.
     */
    public xv_ViewFactory()
    {
        try
        {
            ViewConstructorClasses[0] = Class.forName(ElementClassName);
        }
        catch (ClassNotFoundException  InputException)
        {
    	    eh_Debug.add(2, "xv_ViewFactory constructor: Unable to find element class "
                                  + ElementClassName + ": " + InputException);
        }
    }

    //*****************************************************************************
    /** <p>Create a view of a type suitable for displaying a node whose name is
     *  'InputNodeName'. If no view is registered for InputNodeName, then a
     *  custopmizer suitable for InputNodeType is created. This dual-level scheme
     *  allows special views to be added, while still creating views of
     *  the correct standard type the rest of the time.</p>
     *
     *  <p>Non-standard views can be added by registering them with
     *  xm_NodeTypeRegistry.</p>
     *
     *  @param      InputNode         The node for which we need a view
     *  @return                       The newly-created view for the InputNode
     *  @exception  xv_ViewCreationException  Either no view class is registered
     *                                         or it cannot be instantiated
     */
    public View createView(xg_Node  InputNode) throws xv_ViewCreationException
    {
        View    NewView          = null;
        String  NewViewClassName = getViewClassName(InputNode.getName(),
                                                    InputNode.getType() );
        eh_Debug.add(6, "xv_ViewFactory.createView: Create view of type "
                              + NewViewClassName + " for " + InputNode);

        try
        {
            Class  ViewClass = Class.forName(NewViewClassName);

            // Make sure this class extends the usual View class.
            checkClassType(ViewClass, InputNode.getName(), UsualViewClassName);

            // Find the single-argument (Element) constructor for this View class.
            Constructor  ViewConstructor = ViewClass.getConstructor(ViewConstructorClasses);

            // Create the view itself.
            ViewConstructorArgs[0] = InputNode;
            NewView = (View)ViewConstructor.newInstance(ViewConstructorArgs);
        }
        catch (Exception  InputException)
        {
            eh_Debug.add(2, "xv_ViewFactory: Error creating view of type "
                                 + NewViewClassName + " (" + InputException + ")");
            throw new xv_ViewCreationException("Error creating view of type "
                                                        + NewViewClassName
                                                        + " (" + InputException + ")");
        }

        return NewView;
    }

    //*****************************************************************************
    /** Get the name of the view class suitable for displaying a node
     *  whose name is 'InputNodeName'. If no view is registered for
     *  InputNodeName, get the name of a view suitable for InputNodeType.
     *
     *  @param      InputNodeName  The name of the node for which we need a view
     *  @param      InputNodeType  The type of node for which we need a view
     *  @return                    The newly-created view for the named entity
     *  @exception  xv_ViewCreationException  No view class is registered
     */
    public String getViewClassName(String InputNodeName,
                                   int    InputNodeType) throws xv_ViewCreationException
    {
//	    eh_Debug.add(8, "xv_ViewFactory.getViewClassName: for node name '"
//	                            + InputNodeName + "', type " + InputNodeType);
        xm_NodeTypeDefn  EntityNodeTypeDefn = xm_NodeTypeRegistry.getDefn(InputNodeName);
        if (EntityNodeTypeDefn == null)
            // No entry for this node name - so get one for this node type.
            EntityNodeTypeDefn = xm_NodeTypeRegistry.getDefn(InputNodeType);

        if (EntityNodeTypeDefn == null)
            throw new xv_ViewCreationException("No node type definition registered for "
                                                           + InputNodeName + "/"
                                                           + InputNodeType);

        // Get the name of the class used to view nodes of this type.
        String  NewViewClassName = EntityNodeTypeDefn.getViewClassName();
        if (NewViewClassName == null)
        {
            eh_Debug.add(2, "xv_ViewFactory: No view class registered for "
                                 + InputNodeName);
            throw new xv_ViewCreationException("No view class registered for "
                                                           + InputNodeName);
        }

        return NewViewClassName;
    }

    //*****************************************************************************
    //*ViewFactory*methods*********************************************************
    //*****************************************************************************
    /** Create a view suitable for the InputElement.
     *
     *  @param  InputElement  The element for which a view is required
     *  @return               The newly-created view
     */
    public View create(Element InputElement)
    {
//        eh_Debug.add(6, "xv_ViewFactory.create: Create view for " + InputElement);
        View  NewView = null;
        if (InputElement instanceof xg_Node)
        {
            xg_Node  SourceNode = (xg_Node)InputElement;
            try
            {
                NewView = createView(SourceNode);
            }
            catch (xv_ViewCreationException  InputException)
            {
                eh_Debug.add(2, "xv_ViewFactory.create: Unable to create view for "
                                      + InputElement + ": " + InputException);
            }
        }
        else
    	    NewView = new WrappedPlainView(InputElement);

//    	NewView = new PlainView(InputElement);

    	return NewView;
    }
}

//*****************************************************************************
