tmiller     2002/07/25 04:44:19

  Modified:    java/src/org/apache/xalan/xsltc Translet.java
               java/src/org/apache/xalan/xsltc/compiler CastExpr.java
                        FunctionCall.java Parser.java
               java/src/org/apache/xalan/xsltc/compiler/util
                        ErrorMessages.java ErrorMsg.java NodeSetType.java
  Log:
  bug fix 10837, support of ext java functions
  
  Revision  Changes    Path
  1.4       +3 -1      xml-xalan/java/src/org/apache/xalan/xsltc/Translet.java
  
  Index: Translet.java
  ===================================================================
  RCS file: /home/cvs/xml-xalan/java/src/org/apache/xalan/xsltc/Translet.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- Translet.java     28 Sep 2001 14:39:14 -0000      1.3
  +++ Translet.java     25 Jul 2002 11:44:19 -0000      1.4
  @@ -80,4 +80,6 @@
        throws TransletException;
       public void addAuxiliaryClass(Class auxClass);
       public Class getAuxiliaryClass(String className);
  +    public String[] getNamesArray();
  +    public String[] getNamespaceArray();
   }
  
  
  
  1.13      +7 -1      xml-xalan/java/src/org/apache/xalan/xsltc/compiler/CastExpr.java
  
  Index: CastExpr.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/xsltc/compiler/CastExpr.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- CastExpr.java     28 Jun 2002 15:09:16 -0000      1.12
  +++ CastExpr.java     25 Jul 2002 11:44:19 -0000      1.13
  @@ -109,6 +109,7 @@
        InternalTypeMap.put(Type.NodeSet, Type.String);
        InternalTypeMap.put(Type.NodeSet, Type.Node);
        InternalTypeMap.put(Type.NodeSet, Type.Reference);
  +     InternalTypeMap.put(Type.NodeSet, Type.Object);
   
        InternalTypeMap.put(Type.Node, Type.Node);
        InternalTypeMap.put(Type.Node, Type.Boolean);
  @@ -116,6 +117,7 @@
        InternalTypeMap.put(Type.Node, Type.String);
        InternalTypeMap.put(Type.Node, Type.NodeSet);
        InternalTypeMap.put(Type.Node, Type.Reference);
  +     InternalTypeMap.put(Type.Node, Type.Object);
   
        InternalTypeMap.put(Type.ResultTree, Type.ResultTree);
        InternalTypeMap.put(Type.ResultTree, Type.Boolean);
  @@ -123,6 +125,7 @@
        InternalTypeMap.put(Type.ResultTree, Type.String);
        InternalTypeMap.put(Type.ResultTree, Type.NodeSet);
        InternalTypeMap.put(Type.ResultTree, Type.Reference);
  +     InternalTypeMap.put(Type.ResultTree, Type.Object);
   
        InternalTypeMap.put(Type.Reference, Type.Reference);
        InternalTypeMap.put(Type.Reference, Type.Boolean);
  @@ -132,6 +135,9 @@
        InternalTypeMap.put(Type.Reference, Type.Node);
        InternalTypeMap.put(Type.Reference, Type.NodeSet);
        InternalTypeMap.put(Type.Reference, Type.ResultTree);
  +     InternalTypeMap.put(Type.Reference, Type.Object);
  +
  +     InternalTypeMap.put(Type.Object, Type.String);
   
        InternalTypeMap.put(Type.Void, Type.String);
       }
  
  
  
  1.20      +213 -29   
xml-xalan/java/src/org/apache/xalan/xsltc/compiler/FunctionCall.java
  
  Index: FunctionCall.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/xsltc/compiler/FunctionCall.java,v
  retrieving revision 1.19
  retrieving revision 1.20
  diff -u -r1.19 -r1.20
  --- FunctionCall.java 18 Jul 2002 17:24:43 -0000      1.19
  +++ FunctionCall.java 25 Jul 2002 11:44:19 -0000      1.20
  @@ -60,6 +60,7 @@
    * @author Santiago Pericas-Geertsen
    * @author Morten Jorgensen
    * @author Erwin Bolwidt <[EMAIL PROTECTED]>
  + * @author Todd Miller
    *
    */
   
  @@ -71,8 +72,18 @@
   
   import java.lang.reflect.*;
   
  +import org.apache.bcel.generic.NEW;
  +import org.apache.bcel.generic.IFEQ;
  +import org.apache.bcel.generic.PUSH;
  +import org.apache.bcel.generic.INVOKESTATIC;
  +import org.apache.bcel.generic.INVOKEVIRTUAL;
  +import org.apache.bcel.generic.INVOKESPECIAL;
  +import org.apache.bcel.generic.ConstantPoolGen;
  +import org.apache.bcel.generic.InstructionList;
  +import org.apache.bcel.generic.InstructionConstants;
  +import org.apache.bcel.generic.InvokeInstruction;
  +
   import org.apache.xalan.xsltc.compiler.util.Type;
  -import org.apache.bcel.generic.*;
   import org.apache.xalan.xsltc.compiler.util.*;
   import org.apache.xalan.xsltc.runtime.TransletLoader;
   
  @@ -98,15 +109,23 @@
       protected final static String JAVA_EXT_XALAN =
        "http://xml.apache.org/xslt/java";;
   
  +    /**
  +     * Stores reference to object for non-static Java calls
  +     */
  +    Expression _thisArgument = null;
   
       // External Java function's class/method/signature
  -    private String     _className;
  -    private Method     _chosenMethod;
  -    private MethodType _chosenMethodType;
  +    private String      _className;
  +    private Method      _chosenMethod;
  +    private Constructor _chosenConstructor;
  +    private MethodType  _chosenMethodType;
   
       // Encapsulates all unsupported external function calls
       private boolean    unresolvedExternal;
   
  +    // If FunctionCall is a external java constructor 
  +    private boolean     _isExtConstructor = false; 
  +
       // Legal conversions between internal and Java types.
       private static final MultiHashtable _internal2Java = new MultiHashtable();
   
  @@ -175,6 +194,8 @@
            _java2Internal.put(objectClass, Type.Reference);
   
            // Conversions from org.w3c.dom.Node/NodeList are not supported
  +         // GTM
  +         _java2Internal.put(nodeListClass, Type.NodeSet);
        }
        catch (ClassNotFoundException e) {
            System.err.println(e);
  @@ -242,19 +263,19 @@
        // Handle extension functions (they all have a namespace)
        else {
            try {
  +             // GTM: namespace = http://xml.apache.org/xslt/java
                _className = getClassNameFromUri(namespace);
   
                final int pos = local.lastIndexOf('.');
                if (pos > 0) {
                    _className = _className + local.substring(0, pos);
  -                 _fname = new QName(namespace, null, local.substring(pos + 1));
  +                 _fname = new QName(namespace, null, 
  +                     local.substring(pos + 1));
                }
                else {
                    _fname = new QName(namespace, null, local);
                }
  -             if (_className.length() > 0) {
  -                 return typeCheckExternal(stable);
  -             }
  +             return typeCheckExternal(stable);
            } 
            catch (TypeCheckError e) {
                ErrorMsg errorMsg = e.getErrorMsg();
  @@ -265,20 +286,6 @@
                getParser().reportError(ERROR, errorMsg);
                return _type = Type.Void;
            }
  -
  -         /*
  -          * Warn user if external function could not be resolved.
  -          * Warning will _NOT_ be issued is the call is properly
  -          * wrapped in an <xsl:if> or <xsl:when> element. For details
  -          * see If.parserContents() and When.parserContents()
  -          */
  -         final Parser parser = getParser();
  -         if (parser != null) {
  -             reportWarning(this, parser, ErrorMsg.FUNCTION_RESOLVE_ERR,
  -                           _fname.toString());
  -         }
  -         unresolvedExternal = true;
  -         return _type = Type.Int;    // use "Int" as "unknown"
        }
       }
   
  @@ -315,6 +322,56 @@
        throw new TypeCheckError(this);
       }
   
  +   
  +
  +    public Type typeCheckConstructor(SymbolTable stable) throws TypeCheckError{
  +        final Vector constructors = findConstructors();
  +     if (constructors == null) {
  +            // Constructor not found in this class
  +            throw new TypeCheckError(ErrorMsg.CONSTRUCTOR_NOT_FOUND, 
  +             _className);
  +        
  +     }
  +
  +     final int nConstructors = constructors.size();
  +     final int nArgs = _arguments.size();
  +     final Vector argsType = typeCheckArgs(stable);
  +
  +     // Try all constructors 
  +     for (int j, i = 0; i < nConstructors; i++) {
  +         // Check if all parameters to this constructor can be converted
  +         final Constructor constructor = 
  +             (Constructor)constructors.elementAt(i);
  +         final Class[] paramTypes = constructor.getParameterTypes();
  +
  +         Class extType = null;
  +         for (j = 0; j < nArgs; j++) {
  +             // Convert from internal (translet) type to external (Java) type
  +             extType = paramTypes[j];
  +             final Type intType = (Type)argsType.elementAt(j);
  +             if (!_internal2Java.maps(intType, extType)) break;
  +         }
  +
  +         if (j == nArgs) {
  +             _chosenConstructor = constructor;
  +             _isExtConstructor = true;
  +             return _type = new ObjectType(_className);
  +         }
  +     }
  +
  +     final StringBuffer buf = new StringBuffer(_className);
  +     buf.append('.').append(_fname.getLocalPart()).append('(');
  +     for (int i = 0; i < nArgs; i++) {
  +         final Type intType = (Type)argsType.elementAt(i);
  +         buf.append(intType.toString());
  +         if (i < nArgs - 1) buf.append(", ");
  +     }
  +     buf.append(')');
  +     throw new TypeCheckError(ErrorMsg.ARGUMENT_CONVERSION_ERR, 
  +         buf.toString());
  +    }
  +
  +
       /**
        * Type check a call to an external (Java) method.
        * The method must be static an public, and a legal type conversion
  @@ -323,17 +380,54 @@
        * as a possible candidate.
        */
       public Type typeCheckExternal(SymbolTable stable) throws TypeCheckError {
  +     int nArgs = _arguments.size();
  +     final String name = _fname.getLocalPart();
  +
  +     // check if we are calling an instance method
  +     if (_className.length() == 0) {
  +         if (nArgs > 0) {
  +             _thisArgument = (Expression) _arguments.elementAt(0);
  +             _arguments.remove(0); nArgs--;  
  +             Type type = _thisArgument.typeCheck(stable);    
  +             if (type instanceof ObjectType) {
  +                 _className = ((ObjectType) type).getJavaClassName();
  +             }
  +             else {
  +                 // TODO: define a new error message
  +                    throw new TypeCheckError(ErrorMsg.NO_JAVA_FUNCT_THIS_REF, 
  +                     name);
  +             }
  +            }
  +            else {
  +             /*
  +              * Warn user if external function could not be resolved.
  +              * Warning will _NOT_ be issued is the call is properly
  +              * wrapped in an <xsl:if> or <xsl:when> element. For details
  +              * see If.parserContents() and When.parserContents()
  +              */
  +             final Parser parser = getParser();
  +             if (parser != null) {
  +                 reportWarning(this, parser, ErrorMsg.FUNCTION_RESOLVE_ERR,
  +                               _fname.toString());
  +             }
  +             unresolvedExternal = true;
  +             return _type = Type.Int;        // use "Int" as "unknown"
  +            }             
  +     }
  +     // check if function is a contructor 'new'
  +     else if (_fname.getLocalPart().equals("new")) {
  +         return typeCheckConstructor(stable);
  +     }
  +
        final Vector methods = findMethods();
        
        if (methods == null) {
            // Method not found in this class
  -         final String name = _fname.getLocalPart();
            throw new TypeCheckError(ErrorMsg.METHOD_NOT_FOUND_ERR, name);
        }
   
        Class extType = null;
        final int nMethods = methods.size();
  -     final int nArgs = _arguments.size();
        final Vector argsType = typeCheckArgs(stable);
   
        // Try all methods with the same name as this function
  @@ -359,6 +453,9 @@
                // Use this method if all parameters & return type match
                if (_type != null) {
                    _chosenMethod = method;
  +                 if (_type == Type.NodeSet){
  +                     getXSLTC().setMultiDocument(true);
  +                 }
                    return _type;
                }
            }
  @@ -475,11 +572,50 @@
            il.append(new PUSH(cpg, _fname.toString()));
            il.append(new INVOKESTATIC(index));
        }
  +     else if (_isExtConstructor) {
  +         final String clazz = 
  +             _chosenConstructor.getDeclaringClass().getName();
  +         Class[] paramTypes = _chosenConstructor.getParameterTypes();
  +         
  +         il.append(new NEW(cpg.addClass(_className)));
  +         il.append(InstructionConstants.DUP);
  +
  +         for (int i = 0; i < n; i++) {
  +             final Expression exp = argument(i);
  +             exp.translate(classGen, methodGen);
  +             // Convert the argument to its Java type
  +             exp.startResetIterator(classGen, methodGen);
  +             exp.getType().translateTo(classGen, methodGen, paramTypes[i]);
  +         }
  +
  +         final StringBuffer buffer = new StringBuffer();
  +         buffer.append('(');
  +         for (int i = 0; i < paramTypes.length; i++) {
  +             buffer.append(getSignature(paramTypes[i]));
  +         }
  +         buffer.append(')');
  +         buffer.append("V");
  +
  +         index = cpg.addMethodref(clazz,
  +                                  "<init>", 
  +                                  buffer.toString());
  +         il.append(new INVOKESPECIAL(index));
  +
  +         // Convert the return type back to our internal type
  +         (Type.Object).translateFrom(classGen, methodGen, 
  +                             _chosenConstructor.getDeclaringClass());
  +         
  +     }
        // Invoke function calls that are handled in separate classes
        else {
            final String clazz = _chosenMethod.getDeclaringClass().getName();
            Class[] paramTypes = _chosenMethod.getParameterTypes();
   
  +         // Push "this" if it is an instance method
  +         if (_thisArgument != null) {
  +             _thisArgument.translate(classGen, methodGen);
  +         }
  +
            for (int i = 0; i < n; i++) {
                final Expression exp = argument(i);
                exp.translate(classGen, methodGen);
  @@ -499,7 +635,9 @@
            index = cpg.addMethodref(clazz,
                                     _fname.getLocalPart(),
                                     buffer.toString());
  -         il.append(new INVOKESTATIC(index));
  +         il.append(_thisArgument != null ?
  +             (InvokeInstruction) new INVOKEVIRTUAL(index) : 
  +                (InvokeInstruction) new INVOKESTATIC(index));
   
            // Convert the return type back to our internal type
            _type.translateFrom(classGen, methodGen,
  @@ -549,10 +687,8 @@
   
                    for (int i = 0; i < methods.length; i++) {
                        final int mods = methods[i].getModifiers();
  -
  -                     // Is it public, static and same number of args ?
  +                     // Is it public and same number of args ?
                        if (Modifier.isPublic(mods)
  -                         && Modifier.isStatic(mods)
                            && methods[i].getName().equals(methodName)
                            && methods[i].getParameterTypes().length == nArgs)
                            {
  @@ -572,6 +708,54 @@
        }
        return result;
       }
  +
  +    /**
  +     * Returns a vector with all constructors named <code>_fname</code>
  +     * after stripping its namespace or <code>null</code>
  +     * if no such methods exist.
  +     */
  +    private Vector findConstructors() {
  +        Vector result = null;
  +        final String namespace = _fname.getNamespace();
  +
  +        if (namespace.startsWith(JAVA_EXT_XSLTC) ||
  +            namespace.startsWith(JAVA_EXT_XALAN)) {
  +            final int nArgs = _arguments.size();
  +            try {
  +                TransletLoader loader = new TransletLoader();
  +                final Class clazz = loader.loadClass(_className);
  +
  +                if (clazz == null) {
  +                    final ErrorMsg msg =
  +                        new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className);
  +                    getParser().reportError(Constants.ERROR, msg);
  +                }
  +                else {
  +                    final Constructor[] constructors = clazz.getConstructors();
  +
  +                    for (int i = 0; i < constructors.length; i++) {
  +                        final int mods = constructors[i].getModifiers();
  +                        // Is it public, static and same number of args ?
  +                        if (Modifier.isPublic(mods) &&
  +                           constructors[i].getParameterTypes().length == nArgs)
  +                        {
  +                            if (result == null) {
  +                                result = new Vector();
  +                            }
  +                            result.addElement(constructors[i]);
  +                        }
  +                    }
  +                }
  +            }
  +            catch (ClassNotFoundException e) {
  +                final ErrorMsg msg =
  +                    new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className);
  +                getParser().reportError(Constants.ERROR, msg);
  +            }
  +        }
  +        return result;
  +    }
  +
   
       /**
        * Compute the JVM signature for the class.
  
  
  
  1.51      +3 -3      xml-xalan/java/src/org/apache/xalan/xsltc/compiler/Parser.java
  
  Index: Parser.java
  ===================================================================
  RCS file: /home/cvs/xml-xalan/java/src/org/apache/xalan/xsltc/compiler/Parser.java,v
  retrieving revision 1.50
  retrieving revision 1.51
  diff -u -r1.50 -r1.51
  --- Parser.java       9 Jul 2002 15:37:23 -0000       1.50
  +++ Parser.java       25 Jul 2002 11:44:19 -0000      1.51
  @@ -754,7 +754,7 @@
        MethodType B_V  = new MethodType(Type.Boolean, Type.Void);
        MethodType B_B  = new MethodType(Type.Boolean, Type.Boolean);
        MethodType B_S  = new MethodType(Type.Boolean, Type.String);
  -     MethodType D_T  = new MethodType(Type.NodeSet, Type.ResultTree);
  +     MethodType D_X  = new MethodType(Type.NodeSet, Type.Object);
        MethodType R_RR = new MethodType(Type.Real, Type.Real, Type.Real);
        MethodType I_II = new MethodType(Type.Int, Type.Int, Type.Int);
        MethodType B_RR = new MethodType(Type.Boolean, Type.Real, Type.Real);
  @@ -840,7 +840,7 @@
        _symbolTable.addPrimop("system-property", S_S);
   
        // Extensions
  -     _symbolTable.addPrimop("nodeset", D_T);
  +     _symbolTable.addPrimop("nodeset", D_X);
   
        // Operators +, -, *, /, % defined on real types.
        _symbolTable.addPrimop("+", R_RR);      
  
  
  
  1.12      +7 -3      
xml-xalan/java/src/org/apache/xalan/xsltc/compiler/util/ErrorMessages.java
  
  Index: ErrorMessages.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/xsltc/compiler/util/ErrorMessages.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- ErrorMessages.java        23 May 2002 14:52:09 -0000      1.11
  +++ ErrorMessages.java        25 Jul 2002 11:44:19 -0000      1.12
  @@ -83,7 +83,7 @@
        // CLASS_NOT_FOUND_ERR
        "Cannot find class ''{0}''.",
        // METHOD_NOT_FOUND_ERR
  -     "Cannot find external method ''{0}'' (must be static and public).",
  +     "Cannot find external method ''{0}'' (must be public).",
        // ARGUMENT_CONVERSION_ERR
        "Cannot convert argument/return type in call to method ''{0}''",
        // FILE_NOT_FOUND_ERR
  @@ -265,7 +265,11 @@
        // UNSUPPORTED_ENCODING
        "Output encoding ''{0}'' is not supported on this JVM.",
        // SYNTAX_ERR
  -     "Syntax error in ''{0}''."
  +     "Syntax error in ''{0}''.",
  +     // CONSTRUCTOR_NOT_FOUND 
  +     "Cannot find external constructor ''{0}''.",
  +     // NO_JAVA_FUNCT_THIS_REF 
  +     "First argument to non-static Java function ''{0}'' is not valid object ref."
       };
   
       private static Vector _keys;
  
  
  
  1.14      +3 -1      
xml-xalan/java/src/org/apache/xalan/xsltc/compiler/util/ErrorMsg.java
  
  Index: ErrorMsg.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/xsltc/compiler/util/ErrorMsg.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- ErrorMsg.java     6 May 2002 17:52:55 -0000       1.13
  +++ ErrorMsg.java     25 Jul 2002 11:44:19 -0000      1.14
  @@ -166,6 +166,8 @@
       public static final int STRAY_SORT_ERR          = 74;
       public static final int UNSUPPORTED_ENCODING    = 75;
       public static final int SYNTAX_ERR              = 76;
  +    public static final int CONSTRUCTOR_NOT_FOUND   = 77;
  +    public static final int NO_JAVA_FUNCT_THIS_REF  = 78;
   
       // All error messages are localized and are stored in resource bundles.
       // This array and the following 4 strings are read from that bundle.
  
  
  
  1.10      +47 -1     
xml-xalan/java/src/org/apache/xalan/xsltc/compiler/util/NodeSetType.java
  
  Index: NodeSetType.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/xsltc/compiler/util/NodeSetType.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- NodeSetType.java  1 Feb 2002 20:08:11 -0000       1.9
  +++ NodeSetType.java  25 Jul 2002 11:44:19 -0000      1.10
  @@ -113,6 +113,9 @@
        else if (type == Type.Reference) {
            translateTo(classGen, methodGen, (ReferenceType) type);
        }
  +     else if (type == Type.Object) {
  +         translateTo(classGen, methodGen, (ObjectType) type);
  +     }
        else {
            ErrorMsg err = new ErrorMsg(ErrorMsg.DATA_CONVERSION_ERR,
                                        toString(), type.toString());
  @@ -121,6 +124,39 @@
       }
   
       /**
  +     * Translates an external Java Class into an internal type.
  +     * Expects the Java object on the stack, pushes the internal type
  +     */
  +    public void translateFrom(ClassGenerator classGen, 
  +     MethodGenerator methodGen, Class clazz) 
  +    {
  +             
  +     InstructionList il = methodGen.getInstructionList();
  +     ConstantPoolGen cpg = classGen.getConstantPool();
  +     if (clazz.getName().equals("org.w3c.dom.NodeList")) {
  +        // w3c NodeList is on the stack from the external Java function call.
  +        // call BasisFunction to consume NodeList and leave Iterator on
  +        //    the stack. 
  +        il.append(classGen.loadTranslet());   // push translet onto stack
  +        il.append(methodGen.loadDOM());       // push DOM onto stack
  +        final int convert = cpg.addMethodref(BASIS_LIBRARY_CLASS,
  +                                     "nodeList2Iterator",
  +                                     "("             
  +                                      + "Lorg/w3c/dom/NodeList;"
  +                                      + TRANSLET_INTF_SIG 
  +                                      + DOM_INTF_SIG 
  +                                      + ")" + NODE_ITERATOR_SIG );
  +        il.append(new INVOKESTATIC(convert));
  +     }
  +     else {
  +         ErrorMsg err = new ErrorMsg(ErrorMsg.DATA_CONVERSION_ERR,
  +             toString(), clazz.getName());
  +         classGen.getParser().reportError(Constants.FATAL, err);
  +     } 
  +    }
  +
  +
  +    /**
        * Translates a node-set into a synthesized boolean.
        * The boolean value of a node-set is "true" if non-empty
        * and "false" otherwise. Notice that the 
  @@ -177,6 +213,16 @@
       public void translateTo(ClassGenerator classGen, MethodGenerator methodGen, 
                            NodeType type) {
        getFirstNode(classGen, methodGen);
  +    }
  +
  +    /**
  +     * Subsume node-set into ObjectType.
  +     *
  +     * @see  org.apache.xalan.xsltc.compiler.util.Type#translateTo
  +     */
  +    public void translateTo(ClassGenerator classGen, MethodGenerator methodGen, 
  +                         ObjectType type) {
  +         methodGen.getInstructionList().append(NOP); 
       }
   
       /**
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to