Big issue with bad validation of interfaces when .xsdconfig file is used
------------------------------------------------------------------------

         Key: XMLBEANS-207
         URL: http://issues.apache.org/jira/browse/XMLBEANS-207
     Project: XMLBeans
        Type: Bug
  Components: Compiler  
    Versions: Version 2    
    Reporter: Denis Anisimov


The main problem here is inside InterfaceExtensionImpl when methods 
validateMethods() and validateMethod() are called.
The perform validation against just interface without any knowing of bean 
schema class which will be used for extention.

Lets imagine the situation when schema have type "tOne" and "tTwo" and "tTwo" 
extends "tOne"

<complexType name="tTwo">
    <complexContent>
        <extension base="tOne"/>
    </complexContent>
</complexType>

In this case generated bean TOne will extends TTwo.

I want to create extention for TOne and TTwo. I want:  TOne extends interface 
First, TTwo  extends interface Second. But also I want to keep original 
schema hierarchy , so I want also  interface Second extends First.

Original implementation doesn't allow to me handle this situation : see bug 
http://issues.apache.org/jira/browse/XMLBEANS-205.
If this bug will be resolved then anyway I will need to create corresponding 
methods in SecondHandler methods that will be responsible for
delegation methods from First interface because all methods in First interface 
also exist in Second. BUT TTwo already extends TOne and
I don't need to create static methods for delegation them from First interface. 
They already exists in TTwo because it originally extends TOne. And 
those methods delegated already in handler for First. But configuration file 
knows nothing about schema extentions and validate methiods just against
handlers and interfaces as couple. So we have the problem.

I suggest some changes in InterfaceExtensionImpl.java, SchemaTypeImpl.java, 
SchemaTypeSystemCompiler.java.
Possibly this is not best fix but it works and more appropriate fix wil need 
changes in architecture......
( I suggest that bug http://issues.apache.org/jira/browse/XMLBEANS-205 already 
fixed as described )
( changes marked with "!" )

changes in SchemaTypeSystemCompiler.java :

    public static boolean generateTypes(SchemaTypeSystem system, Filer filer, 
XmlOptions options)
    {
        // partial type systems not allowed to be saved
        if (system instanceof SchemaTypeSystemImpl && 
((SchemaTypeSystemImpl)system).isIncomplete())
            return false;
        
        boolean success = true;

        List types = new ArrayList();
        types.addAll(Arrays.asList(system.globalTypes()));
        types.addAll(Arrays.asList(system.documentTypes()));
        types.addAll(Arrays.asList(system.attributeTypes()));

        for (Iterator i = types.iterator(); i.hasNext(); )
        {
            SchemaType type = (SchemaType)i.next();
            if (type.isBuiltinType())
                continue;
            if (type.getFullJavaName() == null)
                continue;

            String fjn = type.getFullJavaName();

            Writer writer = null;
            
!            // here we perform validation for interfaces that should extends
!            // this type. We check whether all methods in interfaces will be 
!            // present or some methods will be present in supertype, in this 
case
!            // this also Ok.
!            if (!((SchemaTypeImpl)type).validateMethods() ){
!               System.err.println("Was found unimplemented methods. See above 
information");
!               return false;
!
!            }

            try
            {
                // Generate interface class
                writer = filer.createSourceFile(fjn);
                SchemaTypeCodePrinter.printType(writer, type, options);
            }
            catch (IOException e)
            {
                System.err.println("IO Error " + e);
                success = false;
            }
            finally {
                try { if (writer != null) writer.close(); } catch (IOException 
e) {}
            }

            try
            {
                // Generate Implementation class
                fjn = type.getFullJavaImplName();
                writer = filer.createSourceFile(fjn);

                SchemaTypeCodePrinter.printTypeImpl(writer, type, options);
            }
            catch (IOException e)
            {
                System.err.println("IO Error " + e);
                success = false;
            }
            finally {
                try { if (writer != null) writer.close(); } catch (IOException 
e) {}
            }
        }

        return success;
    }

This block is new in SchemaTypeImpl.java :

   /**
     * This method should be called instead of 
<code>InterfaceExtension.validateMethods()</code>
     * for validation interfaces for THIS type. 
     * The reason why  <code>InterfaceExtension.validateMethods()</code> is bad 
:
     * methods from interface can be already implemented in base type. So 
     * we don't need to implement them .  
     */
    public boolean validateMethods(){           
        InterfaceExtension[] extension = getInterfaceExtensions();
        for (int i = 0; i < extension.length; i++) {
                if ( !validateMethods( (InterfaceExtensionImpl)extension[i])) {
                        return false;
                }
                }
        return true;
    }
    
    private boolean validateMethods(InterfaceExtensionImpl extensionImpl) {
        InterfaceExtension.MethodSignature[] methods = 
extensionImpl.getAbsentMethods();
        InterfaceExtension.MethodSignature[] inherited = 
getAllInheritedMethods();
        for (int i = 0; i < methods.length; i++) {
                InterfaceExtension.MethodSignature method = methods[i];         
        
                boolean flag = false;
                        for (int j = 0; j < inherited.length; j++) {            
                
                                if ( extensionImpl.equals( method, 
inherited[j])){
                                        flag = true;
                                }
                        }
                        if ( !flag ){
                                extensionImpl.error(  method );
                                return false;
                        }
                }

        return true;
    }
    
    private InterfaceExtension.MethodSignature[] getAllInheritedMethods(){
        List list = new LinkedList();
        SchemaType type = getBaseType();
        while ( type!= null ){                  
                if (type instanceof SchemaTypeImpl) {
                                SchemaTypeImpl typeImpl = (SchemaTypeImpl) type;
                                InterfaceExtension[] extensions = 
typeImpl.getInterfaceExtensions();
                                if (extensions != null) {
                                        for (int i = 0; i < extensions.length; 
i++) {
                                                
list.addAll(Arrays.asList(extensions[i].getMethods()));
                                        }
                                }
                        }
                type = type.getBaseType();
        }
        
        return (InterfaceExtension.MethodSignature[])
                list.toArray( new 
InterfaceExtension.MethodSignature[list.size()]);
    }

Changes in InterfaceExtensionImpl.java :

New attributes :  

private List myNotFoundMethods = new LinkedList();
private XmlObject myXMLObject ;

Inside method : static InterfaceExtensionImpl newInstance(JamClassLoader 
loader, NameSet xbeanSet, Extensionconfig.Interface intfXO)
   
result.myXMLObject = intfXO;

Changed block ( three new methods and changed old methods validateMethod() and 
validateMethods():

    
    public void error( MethodSignature signature){
        BindingConfigImpl.error("Handler class '" + 
                        _delegateToClassName + "' does not contain method " + 
                        ((MethodSignatureImpl)signature).getSignature(), 
                        myXMLObject);
    }
    
    /**
     * This method perform "light" comparison for two methods signature
     * based only on methods names and signatures. Real comperison in  
     * MethodSignatureImpl perform also checking for outer class name.
     * @param method1
     * @param method2
     * @return
     */
    public boolean equals( InterfaceExtension.MethodSignature method1 ,
                InterfaceExtension.MethodSignature method2 ){
        
        return ( (MethodSignatureImpl)method1).lightEqual( method2 );
    }
    

    /**
     * Changed - will always return true.
     * This is incorrect place for determining correctness of interface.
     * Validation logic is moved into SchemaTypeImpl.validateMethods() 
     * @param interfaceJClass
     * @param delegateJClass
     * @param loc
     * @return
     */
    private boolean validateMethods(JClass interfaceJClass, JClass 
delegateJClass, XmlObject loc)
    {
        //assert _delegateToClass != null : "Delegate to class handler 
expected.";
        boolean valid = true;

        JMethod[] interfaceMethods = interfaceJClass.getMethods();
        List list = new LinkedList(); 
        //_methods = new MethodSignatureImpl[interfaceMethods.length];

        for (int i = 0; i < interfaceMethods.length; i++)
        {
            JMethod method;
                        try {
                                method = validateMethod(interfaceJClass, 
delegateJClass, interfaceMethods[i], loc);
                    if (method != null) {               
                        //_methods[i] = new 
MethodSignatureImpl(getStaticHandler(), method);
                        list.add(new MethodSignatureImpl(getStaticHandler(), 
method));
                    }
                    else {
                        valid = false;
                    }
                        }
                        catch (MethodNotFoundException e) {
                                // we didn't find method. If method was not 
found in static handler
                                // by name and signature - then it was placed 
in myNotFoundMethods list.
                                // in this case validation will be performed 
later. And we don't 
                                // get this exception. But if method was 
actually found in handler,
                                // but its return type or exceptions that it 
can throws is not
                                // the same as declared in interface then we 
got this exception 
                                // and we should stop.
                                return false;
                        }
        }

        _methods = (MethodSignatureImpl[])list.toArray( new 
MethodSignatureImpl[ list.size()] );
        //return valid;
        return true;
    }

    /**
     * Changed.
     * Incorrect place for decision about valid method.
     * Validation logic is moved into SchemaTypeImpl.validateMethods()
     * @param interfaceJClass
     * @param delegateJClass
     * @param method
     * @param loc
     * @return
     */
    private JMethod validateMethod(JClass interfaceJClass, JClass 
delegateJClass, JMethod method, XmlObject loc)
        throws MethodNotFoundException {
        String methodName = method.getSimpleName();
        JParameter[] params = method.getParameters();
        JClass returnType = method.getReturnType();

        JClass[] delegateParams = new JClass[params.length+1];
        delegateParams[0] = returnType.forName("org.apache.xmlbeans.XmlObject");
        for (int i = 1; i < delegateParams.length; i++)
        {
            delegateParams[i] = params[i-1].getType();
        }

        JMethod handlerMethod = null;
        handlerMethod = getMethod(delegateJClass, methodName, delegateParams);  
      
        if (handlerMethod==null)
        {
                MethodSignatureImpl signature = new MethodSignatureImpl( "", 
method);
                myNotFoundMethods.add( signature );
//            BindingConfigImpl.error("Handler class '" + 
delegateJClass.getQualifiedName() + "' does not contain method " + methodName + 
"(" + listTypes(delegateParams) + ")", loc);
            return null;
        }

        // check for throws exceptions
        JClass[] intfExceptions = method.getExceptionTypes();
        JClass[] delegateExceptions = handlerMethod.getExceptionTypes();
        if ( delegateExceptions.length!=intfExceptions.length )
        {
            BindingConfigImpl.error("Handler method '" + 
delegateJClass.getQualifiedName() + "." + methodName + "(" + 
listTypes(delegateParams) +
                ")' must declare the same exceptions as the interface method '" 
+ interfaceJClass.getQualifiedName() + "." + methodName + "(" + 
listTypes(params), loc);
            throw new MethodNotFoundException();
            //return null;
        }

        for (int i = 0; i < delegateExceptions.length; i++)
        {
            if ( delegateExceptions[i]!=intfExceptions[i] )
            {
                BindingConfigImpl.error("Handler method '" + 
delegateJClass.getQualifiedName() + "." + methodName + "(" + 
listTypes(delegateParams) +
                    ")' must declare the same exceptions as the interface 
method '" + interfaceJClass.getQualifiedName() + "." + methodName + "(" + 
listTypes(params), loc);
                throw new MethodNotFoundException();
                //return null;
            }
        }

        if (!handlerMethod.isPublic() || !handlerMethod.isStatic())
        {
            BindingConfigImpl.error("Method '" + 
delegateJClass.getQualifiedName() + "." + methodName + "(" + 
listTypes(delegateParams) + ")' must be declared public and static.", loc);
            throw new MethodNotFoundException();
            //return null;
        }

        if (!returnType.equals(handlerMethod.getReturnType()))
        {
            BindingConfigImpl.error("Return type for method '" + 
handlerMethod.getReturnType() + " " + delegateJClass.getQualifiedName() +
                    "." + methodName + "(" + listTypes(delegateParams) + ")' 
does not match the return type of the interface method :'" + returnType + "'.", 
loc);
            throw new MethodNotFoundException();
            //return null;
        }

        return method;
    }

    public InterfaceExtensionImpl.MethodSignatureImpl[] getAbsentMethods(){
        return (InterfaceExtensionImpl.MethodSignatureImpl[]) 
                myNotFoundMethods.toArray( 
                                new 
InterfaceExtensionImpl.MethodSignatureImpl[myNotFoundMethods.size()]);
    }
    

    
    /**
     * This exception will be thrown if method was FOUND in handler 
     * via name and signature but it have wrong return type, exceptions that
     * can be thrown , etc.  
     * @author ads
     *
     */
    private class MethodNotFoundException extends Exception{
        
    }

I can also send all these changed java files .


-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira


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

Reply via email to