Good question..

what the visitor does is a side-by-side comparison of the the structure of the argument and return type. From that, it detects the genericTypeMatch. Whenever it detects some kind of mismatch, it breaks the parsing.

Of course, all parameter-types (<T>) should be mapped to a real-type (e.g String, Integer, etc).. 

However, the parameter block in front of the method should also be satisfied. 

so:
<T extends Something> T method(TypeX<T> arg);
\—----------------——/
         ^
       this

I got the feeling I needed ‘this' extra check to make sure this condition applies as well. Up to now, I only found a match for <T> (e.g String, Integer, etc).

At forehand I do not know how ‘deep’ a structure is. A visitor pattern is the ideal way to recurse through a tree. You decide what’s interesting in the visitor. In this case, I’m only interested in Type information (e..g methods, classes, etc). But similar patterns exists for Elements. I used a printing visitor to analyze my problem (which I did not commit).

package org.mapstruct.ap.util;

import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.NoType;
import javax.lang.model.type.NullType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.AbstractTypeVisitor6;

/**
 *
 * @author sjaak
 */
public class PrintingTypeVisitor extends AbstractTypeVisitor6<Void, Integer> {

	private String ident(int nrOfPos) {
		StringBuilder b = new StringBuilder();
		for (int i = 0; i < nrOfPos; i++) {
			b.append("..");
		}
		return b.toString();
	}

	private void print(TypeMirror t, Integer nrOfPos) {
		System.out.println(t.getKind() + ", " + t);
	}

	private void print(String s, Integer nrOfPos) {
		System.out.print(ident(nrOfPos)+s);
	}

	public Void scan(TypeMirror t) {
		return this.visit(t, 0);
	}

	@Override
	public Void visitPrimitive(PrimitiveType t, Integer p) {
		print(t, p++);
		return null;
	}

	@Override
	public Void visitNull(NullType t, Integer p) {
		print(t, p++);
		return null;
	}

	@Override
	public Void visitArray(ArrayType t, Integer p) {
		print(t, p++);
		t.getComponentType().accept(this, p);
		return null;
	}

	@Override
	public Void visitDeclared(DeclaredType t, Integer p) {
		print(t, p++);
		for (TypeMirror typeArg : t.getTypeArguments()) {
			print("type args: ", p);
			typeArg.accept(this, p);
		}
		return null;
	}

	@Override
	public Void visitError(ErrorType t, Integer p) {
		print(t, p++);
		for (TypeMirror typeArg : t.getTypeArguments()) {
			print("type args: ", p);
			typeArg.accept(this, p);
		}
		return null;
	}

	@Override
	public Void visitTypeVariable(TypeVariable t, Integer p) {
		print(t, p++);
		print("upper bound: ", p);
		t.getUpperBound().accept(this, p);
		print("lower bound: ", p);
		t.getLowerBound().accept(this, p);
		return null;
	}

	@Override
	public Void visitWildcard(WildcardType t, Integer p) {
		print(t, p++);
		print("type args: ", p);
		t.getExtendsBound().accept(this, p);
		print("type args: ", p);
		t.getSuperBound().accept(this, p);
		return null;
	}

	@Override
	public Void visitExecutable(ExecutableType t, Integer p) {
		print(t, p++);
		for (TypeMirror typeVar : t.getTypeVariables()) {
			print("type vars: ", p);
			typeVar.accept(this, p);
		}
		for (TypeMirror typeArg : t.getParameterTypes()) {
			print("params: ", p);
			typeArg.accept(this, p);
		}
		for (TypeMirror typeArg : t.getThrownTypes()) {
			print("throwns: ", p);
			typeArg.accept(this, p);
		}
		print("return: ", p);
		t.getReturnType().accept(this, p);
		return null;
	}

	@Override
	public Void visitNoType(NoType t, Integer p) {
		print(t, p++);
		return null;
	}
}

Perhaps interesting for Verbose printing ;-)..

Cheers,
Sjaak




On 3 jan. 2014, at 23:52, Gunnar Morling <notifications@github.com> wrote:

> In processor/src/main/java/org/mapstruct/ap/util/MethodMatcher.java:
> 
> > +    private boolean typesMatch = true;
> > +    private Map<TypeVariable, TypeMirror> genericTypesMap = new HashMap<TypeVariable, TypeMirror>();
> > +
> > +    public MethodMatcher(Types typeUtils, Method candidateMethod, Type returnType, Type... arguments) {
> > +        this.typeUtils = typeUtils;
> > +        this.candidateMethod = candidateMethod;
> > +        this.parameters = arguments;
> > +        this.returnType = returnType;
> > +    }
> > +
> > +    public boolean matches() {
> > +
> > +        // check & collect generic types.
> > +        List<? extends VariableElement> candidateParameters = candidateMethod.getExecutable().getParameters();
> > +        if ( candidateParameters.size() == parameters.length ) {
> > +            for ( int i = 0; i < parameters.length; i++ ) {
> Is this loop actually required? For mapping properties we only invoke method with a single parameter (methods with several parameters are only supported as public "entry point" for the user of a mapper).
> 
> Btw. I like the visitor approach. I'm still in the process of digging through everything, but this looks very good :)
> 
> —
> Reply to this email directly or view it on GitHub.
>