sboag       00/06/24 23:35:19

  Modified:    java/src/org/apache/xalan/extensions
                        ExtensionFunctionHandler.java
                        ExtensionNSHandler.java ExtensionsTable.java
                        XSLTJavaClassEngine.java
  Added:       java/src/org/apache/xalan/extensions MethodResolver.java
  Log:
  Make extension functions basically work.  I haven't yet tried extension 
elements.  Implementation of LiveConnect style of Java method resolution.
  
  Revision  Changes    Path
  1.2       +56 -162   
xml-xalan/java/src/org/apache/xalan/extensions/ExtensionFunctionHandler.java
  
  Index: ExtensionFunctionHandler.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/extensions/ExtensionFunctionHandler.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ExtensionFunctionHandler.java     2000/06/19 16:51:58     1.1
  +++ ExtensionFunctionHandler.java     2000/06/25 06:35:18     1.2
  @@ -241,7 +241,7 @@
      */
     public Object callJava (Object object, String method, Object[] args, 
                             Object methodKey) 
  -    throws BSFException
  +    throws SAXException
     {
       // if the function name has a "." in it, then its a static
       // call with the args being arguments to the call. If it 
  @@ -275,11 +275,14 @@
         }
         catch (ClassNotFoundException e) 
         {
  -        throw new BSFException(1, "unable to load class '" + 
  -          className + "'", e);
  +        // throw new BSFException(1, "unable to load class '" + 
  +        //  className + "'", e);
  +        throw new SAXException(e);
         }
       }
  -    
  +    Object[] convertedArgs = (methodArgs.length > 0) 
  +                             ? new Object[methodArgs.length] : null;
  +   
       if((null != m_cachedMethods) && !isNew && (null != object))
       {
         try
  @@ -287,6 +290,9 @@
           Method thisMethod = (Method)m_cachedMethods.get(methodKey);
           if(null != thisMethod)
           {
  +          Class[] paramTypes = thisMethod.getParameterTypes();
  +          MethodResolver.convertParams(methodArgs, convertedArgs, 
paramTypes);
  +                     
             return thisMethod.invoke (object, methodArgs);
           }
         }
  @@ -297,171 +303,56 @@
       
       }
   
  -    // determine the argument types as they are given
  -    Class[] argTypes = null;
  -    if (methodArgs != null) 
  -    {
  -      argTypes = new Class[methodArgs.length];
  -      for (int i = 0; i < argTypes.length; i++) 
  -      {
  -        argTypes[i] = (methodArgs[i]!=null) ? methodArgs[i].getClass() : 
null;
  -      }
  -    }
  -
       // try to find the method and run it, taking into account the special
  -    // type conversions we want. If an arg is a Double, we first try with
  -    // double and then with Double. Same for Boolean/boolean. This is done
  -    // wholesale tho - that is, if there are two Double args both are
  -    // tried double first and then Double.
  -    boolean done = false;
  -    boolean toggled = false;
  +    // type conversions we want.
       try 
       {
  -      while (!done) 
  +      if (isNew) 
         {
  -        if (methodArgs == null) 
  -        {
  -          done = true; // nothing to retry - do as-is or give up
  -        }
  -        else 
  -        {
  -          if (!toggled) 
  -          {
  -            for (int i = 0; i < argTypes.length; i++) 
  -            {
  -              Class cl = argTypes[i];
  -              if (cl != null) 
  -              {
  -                if (cl == Double.class) 
  -                {
  -                  cl = double.class;
  -                }
  -                if (cl == Float.class) 
  -                {
  -                  cl = float.class;
  -                }
  -                else if (cl == Boolean.class) 
  -                {
  -                  cl = boolean.class;
  -                }
  -                else if (cl == Byte.class) 
  -                {
  -                  cl = byte.class;
  -                }
  -                else if (cl == Character.class) 
  -                {
  -                  cl = char.class;
  -                }
  -                else if (cl == Short.class) 
  -                {
  -                  cl = short.class;
  -                }
  -                else if (cl == Integer.class) 
  -                {
  -                  cl = int.class;
  -                }
  -                else if (cl == Long.class) 
  -                {
  -                  cl = long.class;
  -                }
  -                argTypes[i] = cl;
  -              }
  -            }
  -            toggled = true;
  -          }
  -          else 
  -          {
  -            for (int i = 0; i < argTypes.length; i++) 
  -            {
  -              Class cl = argTypes[i];
  -              if (cl != null) 
  -              {
  -                if (cl == double.class) 
  -                {
  -                  cl = Double.class;
  -                }
  -                if (cl == float.class) 
  -                {
  -                  cl = Float.class;
  -                }
  -                else if (cl == boolean.class) 
  -                {
  -                  cl = Boolean.class;
  -                }
  -                else if (cl == byte.class) 
  -                {
  -                  cl = Byte.class;
  -                }
  -                else if (cl == char.class) 
  -                {
  -                  cl = Character.class;
  -                }
  -                else if (cl == short.class) 
  -                {
  -                  cl = Short.class;
  -                }
  -                else if (cl == int.class) 
  -                {
  -                  cl = Integer.class;
  -                }
  -                else if (cl == long.class) 
  -                {
  -                  cl = Long.class;
  -                }
  -                argTypes[i] = cl;
  -              }
  -            }
  -            done = true;
  -          }
  -        }
  -        
  -        // now find method with the right signature, call it and return 
result.
  -        try 
  -        {
  -          if (isNew) 
  -          {
  -            // if its a "new" call then need to find and invoke a constructor
  -            // otherwise find and invoke the appropriate method. The method
  -            // searching logic is the same of course.
  -            Constructor c =
  -                           ReflectionUtils.getConstructor ((Class) object, 
argTypes);
  -            Object obj = c.newInstance (methodArgs);
  -            return obj;
  -          }
  -          else 
  -          {
  -            Method m = ReflectionUtils.getMethod (object, method, argTypes);
  -            Object returnObj = m.invoke (object, methodArgs);
  -            if(!isNew)
  -            {
  -              if(null == m_cachedMethods)
  -                m_cachedMethods = new Hashtable();
  -              m_cachedMethods.put(methodKey, m);
  -            }
  -            return returnObj;
  -          }
  -        }
  -        catch (NoSuchMethodException e) 
  +        // if its a "new" call then need to find and invoke a constructor
  +        // otherwise find and invoke the appropriate method. The method
  +        // searching logic is the same of course.
  +        Constructor c = MethodResolver.getConstructor((Class) object, 
  +                                                      methodArgs, 
convertedArgs);
  +        Object obj = c.newInstance (convertedArgs);
  +        return obj;
  +      }
  +      else 
  +      {
  +        Class cl = (object instanceof Class) ? ((Class)object) : 
object.getClass();
  +        Method m = MethodResolver.getMethod(cl, method,
  +                                            methodArgs, 
  +                                            convertedArgs);
  +        Object returnObj = m.invoke (object, convertedArgs);
  +        if(!isNew)
           {
  -          // ignore if not done looking
  -          if (done) 
  -          {
  -            throw e;
  -          }
  +          if(null == m_cachedMethods)
  +            m_cachedMethods = new Hashtable();
  +          m_cachedMethods.put(methodKey, m);
           }
  +        return returnObj;
         }
       }
  +    catch (NoSuchMethodException nsme) 
  +    {
  +      throw new SAXException(nsme);
  +      // throw new BSFException (BSFException.REASON_OTHER_ERROR,
  +      //  "NoSuchMethodException: " + nsme);
  +    }
       catch (Exception e) 
       {
  +      throw new SAXException(e);
  +      /*
         Throwable t = (e instanceof InvocationTargetException) ?
                       ((InvocationTargetException)e).getTargetException () :
                       null;
         throw new BSFException (BSFException.REASON_OTHER_ERROR,
           "method call/new failed: " + e +
           ((t==null)?"":(" target exception: "+t)), t);
  +      */
       }
       // should not get here
  -    return null;
  +    // return null;
     }
   
     /////////////////////////////////////////////////////////////////////////
  @@ -486,12 +377,10 @@
                                 Object methodKey, Class javaClass)
       throws SAXException 
     {
  -    /*
       if (!componentStarted) 
       {
         startupComponent (javaClass);
       }
  -    */
   
       boolean isJava = false;
       try 
  @@ -509,16 +398,21 @@
         // case 'cause of the funky method selection rules. That engine 
         // expects the first arg to be the object on which to make the call.
         boolean isCTorCall = false;
  -      if (scriptLang.equals ("javaclass")) 
  +      // System.out.println("scriptLang: "+scriptLang);
  +      if (scriptLang.equals ("javaclass") || scriptLang.equals 
("xslt-javaclass")) 
         {
           isJava = true;
  -        isCTorCall = funcName.equals("new");
  +        // isCTorCall = funcName.equals("new");
  +        isCTorCall = funcName.endsWith(".new") || funcName.equals("new");
           e = mgr.loadScriptingEngine ("xslt-javaclass");
           if(isCTorCall)
           {
             argArray = new Object[args.size ()];
             argStart = 0;
  -          funcName = this.classObject.getName()+".new";
  +          if(null != this.classObject)
  +          {
  +            funcName = this.classObject.getName()+".new";
  +          }
             javaObject = null;
             hasCalledCTor = true;
           }
  @@ -559,7 +453,7 @@
           argArray[i+argStart] = 
                                 (o instanceof XObject) ? ((XObject)o).object 
() : o;
         }
  -      
  +
         if(isJava)
           return callJava(javaObject, funcName, argArray, methodKey);
         else
  @@ -574,15 +468,16 @@
           {
             msg = msg.substring("Stopping after fatal error:".length());
           }
  -        System.out.println("Call to extension function failed: "+msg);
  +        // System.out.println("Call to extension function failed: "+msg);
  +        throw new SAXException (e);
         }
         else
         {
           // Should probably make a TRaX Extension Exception.
  -        throw new SAXException ("Extension not found");
  +        throw new SAXException ("Could not create extension: "+funcName+" 
because of: "+e);
         }
       }
  -    return new XNull();
  +    // return new XNull();
     }
   
     /////////////////////////////////////////////////////////////////////////
  @@ -601,8 +496,7 @@
      * 
      * @exception XPathProcessorException if something bad happens.
      */
  -  protected void startupComponent (TransformerImpl transformer,
  -                                   Class classObj) 
  +  protected void startupComponent (Class classObj) 
       throws  SAXException 
     {
       if(!bsfInitialized)
  
  
  
  1.2       +4 -5      
xml-xalan/java/src/org/apache/xalan/extensions/ExtensionNSHandler.java
  
  Index: ExtensionNSHandler.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/extensions/ExtensionNSHandler.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ExtensionNSHandler.java   2000/06/19 16:52:00     1.1
  +++ ExtensionNSHandler.java   2000/06/25 06:35:18     1.2
  @@ -247,7 +247,7 @@
       {
         try
         {
  -        startupComponent (transformer, classObj);
  +        startupComponent (classObj);
         }
         catch (XPathProcessorException e)
         {
  @@ -292,8 +292,7 @@
      *
      * @exception XPathProcessorException if something bad happens.
      */
  -  protected void startupComponent (TransformerImpl transformer, 
  -                                   Class classObj) 
  +  protected void startupComponent (Class classObj) 
       throws  SAXException
     {
       if(!bsfInitialized)
  @@ -318,7 +317,7 @@
           throw new XPathProcessorException (e.getMessage (), e);
         }
       }
  -    super.startupComponent (transformer, classObj);
  +    super.startupComponent (classObj);
     }
   
     /////////////////////////////////////////////////////////////////////////
  @@ -354,7 +353,7 @@
       }
   
       // parse the document at the URI of the extension, if any
  -    URL url = null; // xslp.getURLFromString(namespaceUri,
  +    String url = null; // xslp.getAbsoluteURI(namespaceUri,
                        //               
xslp.m_stylesheetRoot.getBaseIdentifier());
       // System.out.println("Extension URI: "+url.toString());
       org.apache.xalan.xpath.XPathContext liaison = xslp.getXPathContext();
  
  
  
  1.2       +1 -0      
xml-xalan/java/src/org/apache/xalan/extensions/ExtensionsTable.java
  
  Index: ExtensionsTable.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/extensions/ExtensionsTable.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ExtensionsTable.java      2000/06/19 16:52:00     1.1
  +++ ExtensionsTable.java      2000/06/25 06:35:18     1.2
  @@ -246,6 +246,7 @@
               }
               System.out.println("Call to extension function failed: "+msg);
               result = new XNull();
  +            throw new org.xml.sax.SAXException(e);
             }
           }
         }
  
  
  
  1.2       +23 -142   
xml-xalan/java/src/org/apache/xalan/extensions/XSLTJavaClassEngine.java
  
  Index: XSLTJavaClassEngine.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/extensions/XSLTJavaClassEngine.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- XSLTJavaClassEngine.java  2000/06/19 16:52:00     1.1
  +++ XSLTJavaClassEngine.java  2000/06/25 06:35:18     1.2
  @@ -130,153 +130,34 @@
         }
       }
   
  -    // determine the argument types as they are given
  -    Class[] argTypes = null;
  -    if (methodArgs != null) 
  +    Object[] convertedArgs = (methodArgs.length > 0) 
  +                             ? new Object[methodArgs.length] : null;
  +    try 
       {
  -      argTypes = new Class[methodArgs.length];
  -      for (int i = 0; i < argTypes.length; i++) 
  +      if (isNew) 
         {
  -        argTypes[i] = (methodArgs[i]!=null) ? methodArgs[i].getClass() : 
null;
  +        // if its a "new" call then need to find and invoke a constructor
  +        // otherwise find and invoke the appropriate method. The method
  +        // searching logic is the same of course.
  +        Constructor c = MethodResolver.getConstructor((Class) object, 
  +                                                      methodArgs, 
convertedArgs);
  +        Object obj = c.newInstance (methodArgs);
  +        return obj;
         }
  -    }
  -
  -    // try to find the method and run it, taking into account the special
  -    // type conversions we want. If an arg is a Double, we first try with
  -    // double and then with Double. Same for Boolean/boolean. This is done
  -    // wholesale tho - that is, if there are two Double args both are
  -    // tried double first and then Double.
  -    boolean done = false;
  -    boolean toggled = false;
  -    try 
  -    {
  -      while (!done) 
  +      else 
         {
  -        if (methodArgs == null) 
  -        {
  -          done = true; // nothing to retry - do as-is or give up
  -        }
  -        else 
  -        {
  -          if (!toggled) 
  -          {
  -            for (int i = 0; i < argTypes.length; i++) 
  -            {
  -              Class cl = argTypes[i];
  -              if (cl != null) 
  -              {
  -                if (cl == Double.class) 
  -                {
  -                  cl = double.class;
  -                }
  -                if (cl == Float.class) 
  -                {
  -                  cl = float.class;
  -                }
  -                else if (cl == Boolean.class) 
  -                {
  -                  cl = boolean.class;
  -                }
  -                else if (cl == Byte.class) 
  -                {
  -                  cl = byte.class;
  -                }
  -                else if (cl == Character.class) 
  -                {
  -                  cl = char.class;
  -                }
  -                else if (cl == Short.class) 
  -                {
  -                  cl = short.class;
  -                }
  -                else if (cl == Integer.class) 
  -                {
  -                  cl = int.class;
  -                }
  -                else if (cl == Long.class) 
  -                {
  -                  cl = long.class;
  -                }
  -                argTypes[i] = cl;
  -              }
  -            }
  -            toggled = true;
  -          }
  -          else 
  -          {
  -            for (int i = 0; i < argTypes.length; i++) 
  -            {
  -              Class cl = argTypes[i];
  -              if (cl != null) 
  -              {
  -                if (cl == double.class) 
  -                {
  -                  cl = Double.class;
  -                }
  -                if (cl == float.class) 
  -                {
  -                  cl = Float.class;
  -                }
  -                else if (cl == boolean.class) 
  -                {
  -                  cl = Boolean.class;
  -                }
  -                else if (cl == byte.class) 
  -                {
  -                  cl = Byte.class;
  -                }
  -                else if (cl == char.class) 
  -                {
  -                  cl = Character.class;
  -                }
  -                else if (cl == short.class) 
  -                {
  -                  cl = Short.class;
  -                }
  -                else if (cl == int.class) 
  -                {
  -                  cl = Integer.class;
  -                }
  -                else if (cl == long.class) 
  -                {
  -                  cl = Long.class;
  -                }
  -                argTypes[i] = cl;
  -              }
  -            }
  -            done = true;
  -          }
  -        }
  -        
  -        // now find method with the right signature, call it and return 
result.
  -        try 
  -        {
  -          if (isNew) 
  -          {
  -            // if its a "new" call then need to find and invoke a constructor
  -            // otherwise find and invoke the appropriate method. The method
  -            // searching logic is the same of course.
  -            Constructor c =
  -                           ReflectionUtils.getConstructor ((Class) object, 
argTypes);
  -            Object obj = c.newInstance (methodArgs);
  -            return obj;
  -          }
  -          else 
  -          {
  -            Method m = ReflectionUtils.getMethod (object, method, argTypes);
  -            return m.invoke (object, methodArgs);
  -          }
  -        }
  -        catch (NoSuchMethodException e) 
  -        {
  -          // ignore if not done looking
  -          if (done) 
  -          {
  -            throw e;
  -          }
  -        }
  +        Method m = MethodResolver.getMethod(object.getClass(), method,
  +                                                      methodArgs, 
  +                                                      convertedArgs);
  +        return m.invoke (object, methodArgs);
         }
       }
  +    catch (NoSuchMethodException nsme) 
  +    {
  +      // ignore if not done looking
  +      throw new BSFException (BSFException.REASON_OTHER_ERROR,
  +        "NoSuchMethodException: " + nsme);
  +    }
       catch (Exception e) 
       {
         Throwable t = (e instanceof InvocationTargetException) ?
  @@ -287,7 +168,7 @@
           ((t==null)?"":(" target exception: "+t)), t);
       }
       // should not get here
  -    return null;
  +    // return null;
     }
   }
   
  
  
  
  1.1                  
xml-xalan/java/src/org/apache/xalan/extensions/MethodResolver.java
  
  Index: MethodResolver.java
  ===================================================================
  package org.apache.xalan.extensions;
  
  import java.lang.reflect.Method;
  import java.lang.reflect.Constructor;
  
  import org.w3c.dom.Node;
  import org.w3c.dom.traversal.NodeIterator;
  
  import org.apache.xalan.xpath.XObject;
  import org.apache.xalan.xpath.XString;
  
  /**
   * Utility class to help resolve method overloading with Xalan XSLT 
   * argument types.
   */
  public class MethodResolver
  {
    /**
     * Given a class, figure out the resolution of 
     * the Java Constructor from the XSLT argument types, and perform the 
     * conversion of the arguments.
     * @param classObj the Class of the object to be constructed.
     * @param argsIn An array of XSLT/XPath arguments.
     * @param argsOut An array of the exact size as argsIn, which will be 
     * populated with converted arguments if a suitable method is found.
     * @return A constructor that will work with the argsOut array.
     * @exception SAXException may be thrown for Xalan conversion
     * exceptions.
     */
    public static Constructor getConstructor(Class classObj, 
                                             Object[] argsIn, 
                                             Object[] argsOut)
      throws NoSuchMethodException,
             SecurityException,
             org.xml.sax.SAXException
    {
      Constructor bestConstructor = null;
      Class[] bestParamTypes = null;
      Constructor[] constructors = classObj.getConstructors();
      int nMethods = constructors.length;
      int bestScore = Integer.MAX_VALUE;
      for(int i = 0; i < nMethods; i++)
      {
        Constructor ctor = constructors[i];
        Class[] paramTypes = ctor.getParameterTypes();
        if(argsIn.length == paramTypes.length)
        {
          // then we have our candidate.
          int score = scoreMatch(paramTypes, argsIn);
          if(-1 == score)
            continue;
          if(score < bestScore)
          {
            bestConstructor = ctor;
            bestParamTypes = paramTypes;
            bestScore = score;
          }
        }
      }
  
      if(null == bestConstructor)
        throw new NoSuchMethodException(classObj.getName()); // Should give 
more info...
      else
        convertParams(argsIn, argsOut, bestParamTypes);
      
      return bestConstructor;
    }
  
    
    /**
     * Given the name of a method, figure out the resolution of 
     * the Java Method from the XSLT argument types, and perform the 
     * conversion of the arguments.
     * @param classObj The Class of the object that should have the method.
     * @param name The name of the method to be invoked.
     * @param argsIn An array of XSLT/XPath arguments.
     * @param argsOut An array of the exact size as argsIn, which will be 
     * populated with converted arguments if a suitable method is found.
     * @return A method that will work with the argsOut array.
     * @exception SAXException may be thrown for Xalan conversion
     * exceptions.
     */
    public static Method getMethod(Class classObj, String name, 
                                   Object[] argsIn, 
                                   Object[] argsOut)
      throws NoSuchMethodException,
             SecurityException,
             org.xml.sax.SAXException
    {
      Method bestMethod = null;
      Class[] bestParamTypes = null;
      Method[] methods = classObj.getMethods();
      int nMethods = methods.length;
      int bestScore = Integer.MAX_VALUE;
      for(int i = 0; i < nMethods; i++)
      {
        Method method = methods[i];
        if(method.getName().equals(name))
        {
          Class[] paramTypes = method.getParameterTypes();
          if(argsIn.length == paramTypes.length)
          {
            // then we have our candidate.
            int score = scoreMatch(paramTypes, argsIn);
            if(-1 == score)
              continue;
            if(score < bestScore)
            {
              bestMethod = method;
              bestParamTypes = paramTypes;
              bestScore = score;
            }
          }
        }
      }
      
      if(null == bestMethod)
        throw new NoSuchMethodException(name); // Should give more info...
      else
        convertParams(argsIn, argsOut, bestParamTypes);
      
      return bestMethod;
    }
    
    /**
     * Convert a set of parameters based on a set of paramTypes.
     * @param argsIn An array of XSLT/XPath arguments.
     * @param argsOut An array of the exact size as argsIn, which will be 
     * populated with converted arguments.
     * @param paramTypes An array of class objects, of the exact same 
     * size as argsIn and argsOut.
     * @exception SAXException may be thrown for Xalan conversion
     * exceptions.
     */
    public static void convertParams(Object[] argsIn, 
                       Object[] argsOut, Class[] paramTypes)
      throws org.xml.sax.SAXException
    {
      int nMethods = argsIn.length;
      for(int i = 0; i < nMethods; i++)
      {
        argsOut[i] = convert(argsIn[i], paramTypes[i]);
      }
    }
    
    /**
     * Simple class to hold information about allowed conversions 
     * and their relative scores, for use by the table below.
     */
    static class ConversionInfo
    {
      ConversionInfo(Class cl, int score)
      {
        m_class = cl;
        m_score = score;
      }
      
      Class m_class;  // Java class to convert to.
      int m_score; // Match score, closer to zero is more matched.
    }
    
    /**
     * Specification of conversions from XSLT type CLASS_UNKNOWN
     * (i.e. some unknown Java object) to allowed Java types.
     */
    static ConversionInfo[] m_javaObjConversions = {
      new ConversionInfo(java.lang.Object.class, 0),
      new ConversionInfo(Double.TYPE, 1),
      new ConversionInfo(Float.TYPE, 2),
      new ConversionInfo(Long.TYPE, 3),
      new ConversionInfo(Integer.TYPE, 4),
      new ConversionInfo(Short.TYPE, 5),
      new ConversionInfo(Character.TYPE, 6),
      new ConversionInfo(Byte.TYPE, 7),
      new ConversionInfo(java.lang.String.class, 8)
    };
      
    /**
     * Specification of conversions from XSLT type CLASS_BOOLEAN
     * to allowed Java types.
     */
    static ConversionInfo[] m_booleanConversions = {
      new ConversionInfo(Boolean.TYPE, 0),
      new ConversionInfo(java.lang.Boolean.class, 1),
      new ConversionInfo(java.lang.Object.class, 1),
      new ConversionInfo(java.lang.String.class, 2)
      };
  
    /**
     * Specification of conversions from XSLT type CLASS_NUMBER
     * to allowed Java types.
     */
    static ConversionInfo[] m_numberConversions = {
      new ConversionInfo(Double.TYPE, 0),
      new ConversionInfo(java.lang.Double.class, 1),
      new ConversionInfo(Float.TYPE, 3),
      new ConversionInfo(Long.TYPE, 4),
      new ConversionInfo(Integer.TYPE, 5),
      new ConversionInfo(Short.TYPE, 6),
      new ConversionInfo(Character.TYPE, 7),
      new ConversionInfo(Byte.TYPE, 8),
      new ConversionInfo(java.lang.String.class, 9),
      new ConversionInfo(java.lang.Object.class, 10)
    };
  
    /**
     * Specification of conversions from XSLT type CLASS_STRING
     * to allowed Java types.
     */
    static ConversionInfo[] m_stringConversions = {
      new ConversionInfo(java.lang.String.class, 0),
      new ConversionInfo(java.lang.Object.class, 1),
      new ConversionInfo(Character.TYPE, 2),
      new ConversionInfo(Double.TYPE, 3),
      new ConversionInfo(Float.TYPE, 3),
      new ConversionInfo(Long.TYPE, 3),
      new ConversionInfo(Integer.TYPE, 3),
      new ConversionInfo(Short.TYPE, 3),
      new ConversionInfo(Byte.TYPE, 3)
    };
  
    /**
     * Specification of conversions from XSLT type CLASS_RTREEFRAG
     * to allowed Java types.
     */
    static ConversionInfo[] m_rtfConversions = {
      new ConversionInfo(org.w3c.dom.traversal.NodeIterator.class, 0),
      new ConversionInfo(org.w3c.dom.Node.class, 1),
      new ConversionInfo(java.lang.String.class, 2),
      new ConversionInfo(Boolean.TYPE, 3),
      new ConversionInfo(java.lang.Object.class, 4),
      new ConversionInfo(Character.TYPE, 5),
      new ConversionInfo(Double.TYPE, 6),
      new ConversionInfo(Float.TYPE, 6),
      new ConversionInfo(Long.TYPE, 6),
      new ConversionInfo(Integer.TYPE, 6),
      new ConversionInfo(Short.TYPE, 6),
      new ConversionInfo(Byte.TYPE, 6)
    };
    
    /**
     * Specification of conversions from XSLT type CLASS_NODESET
     * to allowed Java types.  (This is the same as for CLASS_RTREEFRAG)
     */
    static ConversionInfo[] m_nodesetConversions = {
      new ConversionInfo(org.w3c.dom.traversal.NodeIterator.class, 0),
      new ConversionInfo(org.w3c.dom.Node.class, 1),
      new ConversionInfo(java.lang.String.class, 2),
      new ConversionInfo(Boolean.TYPE, 3),
      new ConversionInfo(java.lang.Object.class, 4),
      new ConversionInfo(Character.TYPE, 5),
      new ConversionInfo(Double.TYPE, 6),
      new ConversionInfo(Float.TYPE, 6),
      new ConversionInfo(Long.TYPE, 6),
      new ConversionInfo(Integer.TYPE, 6),
      new ConversionInfo(Short.TYPE, 6),
      new ConversionInfo(Byte.TYPE, 6)
    };
    
    /**
     * Order is significant in the list below, based on 
     * XObject.CLASS_XXX values.
     */
    static ConversionInfo[][] m_conversions = 
    {
      m_javaObjConversions, // CLASS_UNKNOWN = 0;
      m_booleanConversions, // CLASS_BOOLEAN = 1;
      m_numberConversions,  // CLASS_NUMBER = 2;
      m_stringConversions,  // CLASS_STRING = 3;
      m_nodesetConversions, // CLASS_NODESET = 4;
      m_rtfConversions      // CLASS_RTREEFRAG = 5;
    };
    
    /**
     * Score the conversion of a set of XSLT arguments to a 
     * given set of Java parameters.
     * If any invocations of this function for a method with 
     * the same name return the same positive value, then a conflict 
     * has occured, and an error should be signaled.
     * @param javeParamTypes Must be filled with valid class names, and 
     * of the same length as xsltArgs.
     * @param xsltArgs Must be filled with valid object instances, and 
     * of the same length as javeParamTypes.
     * @return -1 for no allowed conversion, or a positive score 
     * that is closer to zero for more preferred, or further from 
     * zero for less preferred.
     */
    public static int scoreMatch(Class[] javeParamTypes, 
                                 Object[] xsltArgs)
    {
      int nParams = xsltArgs.length;
      int score = 0;
      for(int i = 0; i < nParams; i++)
      {
        Object xsltObj = xsltArgs[i];
        int xsltClassType = (xsltObj instanceof XObject) 
                            ? ((XObject)xsltObj).getType() 
                              : XObject.CLASS_UNKNOWN;
        Class javaClass = javeParamTypes[i];
        
        if(xsltClassType == XObject.CLASS_NULL)
        {
          // In Xalan I have objects of CLASS_NULL, though I'm not 
          // sure they're used any more.  For now, do something funky.
          if(!javaClass.isPrimitive())
          {
            // Then assume that a null can be used, but give it a low score.
            score += 10;
            continue;
          }
          else
            return -1;  // no match.
        }
        
        ConversionInfo[] convInfo = m_conversions[xsltClassType];
        int nConversions = convInfo.length;
        int k;
        for(k = 0; k < nConversions; k++)
        {
          ConversionInfo cinfo = convInfo[k];
          if(javaClass.isAssignableFrom(cinfo.m_class))
          {
            score += cinfo.m_score;
            break; // from k loop
          }
        }
        if(k == nConversions)
          return -1; // no match
      }
      return score;
    }
    
    /**
     * Convert the given XSLT object to an object of 
     * the given class.
     * @param xsltObj The XSLT object that needs conversion.
     * @param javaClass The type of object to convert to.
     * @returns An object suitable for passing to the Method.invoke 
     * function in the args array, which may be null in some cases.
     * @exception SAXException may be thrown for Xalan conversion
     * exceptions.
     */
    static Object convert(Object xsltObj, Class javaClass)
      throws org.xml.sax.SAXException
    {
      if(xsltObj instanceof XObject)
      {
        XObject xobj = ((XObject)xsltObj);
        int xsltClassType = xobj.getType();
  
        switch(xsltClassType)
        {
        case XObject.CLASS_NULL:
          return null;
          
        case XObject.CLASS_BOOLEAN:
          {
            if(javaClass == java.lang.String.class)
              return xobj.str();
            else
              return new Boolean(xobj.bool());
          }
          // break; Unreachable
        case XObject.CLASS_NUMBER:
          {
            if(javaClass == java.lang.String.class)
              return xobj.str();
            else 
            {
              return convertDoubleToNumber(xobj.num(), javaClass);
            }
          }
          // break; Unreachable
          
        case XObject.CLASS_STRING:
          {
            if((javaClass == java.lang.String.class) ||
               (javaClass == java.lang.Object.class))
              return xobj.str();
            else if(javaClass == Character.TYPE)
            {
              String str = xobj.str();
              if(str.length() > 0)
                return new Character(str.charAt(0));
              else
                return null; // ??
            }
            else 
            {
              return convertDoubleToNumber(xobj.num(), javaClass);
            }
          }
          // break; Unreachable
          
        case XObject.CLASS_RTREEFRAG:
          {
            if((javaClass.isAssignableFrom(NodeIterator.class)) ||
               (javaClass == java.lang.Object.class))
            {
              // This will fail in Xalan right now, since RTFs aren't 
              // convertable to node-sets.
              return xobj.nodeset();
            }
            else if(javaClass.isAssignableFrom(Node.class))
            {
              // This will return a Document fragment in Xalan right 
              // now, which isn't what the we specify.
              return xobj.rtree();
            }
            else if(javaClass == java.lang.String.class)
            {
              return xobj.str();
            }
            else if(javaClass == Boolean.TYPE)
            {
              return new Boolean(xobj.bool());
            }
            else
            {
              return convertDoubleToNumber(xobj.num(), javaClass);
            }
          }
          // break; Unreachable
          
        case XObject.CLASS_NODESET:
          {
            if((javaClass.isAssignableFrom(NodeIterator.class)) ||
               (javaClass == java.lang.Object.class))
            {
              // This will fail in Xalan right now, since RTFs aren't 
              // convertable to node-sets.
              return xobj.nodeset();
            }
            else if(javaClass.isAssignableFrom(Node.class))
            {
              // Xalan ensures that nodeset() always returns an
              // iterator positioned at the beginning.
              NodeIterator ni = xobj.nodeset();
              return ni.nextNode(); // may be null.
            }
            else if(javaClass == java.lang.String.class)
            {
              return xobj.str();
            }
            else if(javaClass == Boolean.TYPE)
            {
              return new Boolean(xobj.bool());
            }
            else
            {
              return convertDoubleToNumber(xobj.num(), javaClass);
            }
          }
          // break; Unreachable
        
          // No default:, fall-through on purpose
        } // end switch
        xsltObj = xobj.object();
        
      } // end if if(xsltObj instanceof XObject)
      
      // At this point, we have a raw java object.
      if(javaClass == java.lang.String.class)
      {
        return xsltObj.toString();
      }
      else if(javaClass.isPrimitive())
      {
        // Assume a number conversion
        XString xstr = new XString(xsltObj.toString());
        double num = xstr.num();
        return convertDoubleToNumber(num, javaClass);
      }
      else
      {
        // Just pass the object directly, and hope for the best.
        return xsltObj;
      }
    }
    
    /**
     * Do a standard conversion of a double to the specified type.
     * @param num The number to be converted.
     * @param javaClass The class type to be converted to.
     * @return An object specified by javaClass, or a Double instance.
     */
    static Object convertDoubleToNumber(double num, Class javaClass)
    {
      // In the code below, I don't check for NaN, etc., instead 
      // using the standard Java conversion, as I think we should 
      // specify.  See issue-runtime-errors.
      if((javaClass == Double.TYPE) ||
         (javaClass == java.lang.Double.class))
        return new Double(num);
      else if(javaClass == Float.TYPE)
        return new Float(num);
      else if(javaClass == Long.TYPE)
      {
        // Use standard Java Narrowing Primitive Conversion
        // See http://java.sun.com/docs/books/jls/html/5.doc.html#175672
        return new Long((long)num);
      }
      else if(javaClass == Integer.TYPE)
      {
        // Use standard Java Narrowing Primitive Conversion
        // See http://java.sun.com/docs/books/jls/html/5.doc.html#175672
        return new Integer((int)num);
      }
      else if(javaClass == Short.TYPE)
      {
        // Use standard Java Narrowing Primitive Conversion
        // See http://java.sun.com/docs/books/jls/html/5.doc.html#175672
        return new Short((short)num);
      }
      else if(javaClass == Character.TYPE)
      {
        // Use standard Java Narrowing Primitive Conversion
        // See http://java.sun.com/docs/books/jls/html/5.doc.html#175672
        return new Character((char)num);
      }
      else if(javaClass == Byte.TYPE)
      {
        // Use standard Java Narrowing Primitive Conversion
        // See http://java.sun.com/docs/books/jls/html/5.doc.html#175672
        return new Byte((byte)num);
      }
      else
      {
        // Should never get here??
        return new Double(num);
      }
    }
  
  }
  
  
  

Reply via email to