- Revision
- 726
- Author
- rfscholte
- Date
- 2010-04-19 07:57:29 -0500 (Mon, 19 Apr 2010)
Log Message
QDOX-207: resolve generic Type
Modified Paths
- trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaClass.java
- trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaMethod.java
- trunk/qdox/src/java/com/thoughtworks/qdox/model/Type.java
- trunk/qdox/src/test/com/thoughtworks/qdox/GenericsTest.java
Added Paths
Diff
Modified: trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaClass.java (725 => 726)
--- trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaClass.java 2010-03-30 16:04:01 UTC (rev 725) +++ trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaClass.java 2010-04-19 12:57:29 UTC (rev 726) @@ -466,7 +466,7 @@ // todo: ideally we should check on package privacy too. oh well. if ((method != null) && !method.isPrivate()) { - result.add(method); + result.add( new JavaMethodDelegate( this, method ) ); } } @@ -477,7 +477,7 @@ parameterTypes, true, varArg ); if (method != null) { - result.add(method); + result.add( new JavaMethodDelegate( this, method ) ); } } }
Modified: trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaMethod.java (725 => 726)
--- trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaMethod.java 2010-03-30 16:04:01 UTC (rev 725) +++ trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaMethod.java 2010-04-19 12:57:29 UTC (rev 726) @@ -1,9 +1,9 @@ package com.thoughtworks.qdox.model; import java.beans.Introspector; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import java.util.ArrayList; public class JavaMethod extends AbstractInheritableJavaEntity implements Member { @@ -387,4 +387,54 @@ } return result.toString(); } + + /** + * Equivalent of java.lang.reflect.Method.getGenericReturnType() + * + * @return the generic returntype + * @since 1.12.1 + */ + public Type getGenericReturnType() + { + return returns; + } + + /** + * Equivalent of java.lang.reflect.Method.getReturnType() + * + * @return + * @since 1.12.1 + */ + public Type getReturnType() { + return getReturnType( false ); + } + + /** + * + * @param resolve + * @return + * @since 1.12.1 + */ + public Type getReturnType( boolean resolve ) + { + return getReturnType( resolve, getParentClass() ); + } + + /** + * + * @param resolve + * @param callingClass + * @return + * @since 1.12.1 + */ + protected Type getReturnType ( boolean resolve, JavaClass callingClass) { + Type result = getReturns().resolve( this.getParentClass(), callingClass ); + + //According to java-specs, if it could be resolved the upper boundary, so Object, should be returned + if ( !resolve && !returns.getFullyQualifiedName().equals( result.getFullyQualifiedName() ) ) + { + result = new Type( "java.lang.Object" ); + } + return result; + } }
Added: trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaMethodDelegate.java (0 => 726)
--- trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaMethodDelegate.java (rev 0) +++ trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaMethodDelegate.java 2010-04-19 12:57:29 UTC (rev 726) @@ -0,0 +1,346 @@ +package com.thoughtworks.qdox.model; + +import java.util.List; + +/** + * This class can be used to access overridden methods while keeping a reference to the original class. + * This is especially useful when trying to resolve generics + * + * @author Robert Scholte + * @since 1.12.1 + */ +public class JavaMethodDelegate extends JavaMethod +{ + + private JavaClass callingClass; + private JavaMethod originalMethod; + + public JavaMethodDelegate( JavaClass callingClass, JavaMethod originalMethod ) + { + this.callingClass = callingClass; + this.originalMethod = originalMethod; + } + + public Type getReturnType( boolean resolve ) + { + Type returnType = originalMethod.getReturnType( resolve, callingClass ); //TEntity + return returnType.resolve( originalMethod.getParentClass(), callingClass ); + } + + protected Type getReturnType( boolean resolve, JavaClass callingClass ) + { + return super.getReturnType( resolve, this.callingClass ); + } + + //Delegating methods + + public void addParameter( JavaParameter javaParameter ) + { + originalMethod.addParameter( javaParameter ); + } + + public int compareTo( Object o ) + { + return originalMethod.compareTo( o ); + } + + public boolean equals( Object obj ) + { + return originalMethod.equals( obj ); + } + + public Annotation[] getAnnotations() + { + return originalMethod.getAnnotations(); + } + + public String getCallSignature() + { + return originalMethod.getCallSignature(); + } + + public String getCodeBlock() + { + return originalMethod.getCodeBlock(); + } + + public String getComment() + { + return originalMethod.getComment(); + } + + public String getDeclarationSignature( boolean withModifiers ) + { + return originalMethod.getDeclarationSignature( withModifiers ); + } + + public Type[] getExceptions() + { + return originalMethod.getExceptions(); + } + + public Type getGenericReturnType() + { + return originalMethod.getGenericReturnType(); + } + + public int getLineNumber() + { + return originalMethod.getLineNumber(); + } + + public String[] getModifiers() + { + return originalMethod.getModifiers(); + } + + public String getName() + { + return originalMethod.getName(); + } + + public String getNamedParameter( String tagName, String parameterName ) + { + return originalMethod.getNamedParameter( tagName, parameterName ); + } + + public JavaParameter getParameterByName( String name ) + { + return originalMethod.getParameterByName( name ); + } + + public JavaParameter[] getParameters() + { + return originalMethod.getParameters(); + } + + public JavaClassParent getParent() + { + return originalMethod.getParent(); + } + + public JavaClass getParentClass() + { + return originalMethod.getParentClass(); + } + + public String getPropertyName() + { + return originalMethod.getPropertyName(); + } + + public Type getPropertyType() + { + return originalMethod.getPropertyType(); + } + + public Type getReturns() + { + return originalMethod.getReturns(); + } + + public Type getReturnType() + { + return getReturnType( false ); + } + + public JavaSource getSource() + { + return originalMethod.getSource(); + } + + public String getSourceCode() + { + return originalMethod.getSourceCode(); + } + + public DocletTag getTagByName( String name, boolean inherited ) + { + return originalMethod.getTagByName( name, inherited ); + } + + public DocletTag getTagByName( String name ) + { + return originalMethod.getTagByName( name ); + } + + public DocletTag[] getTags() + { + return originalMethod.getTags(); + } + + public DocletTag[] getTagsByName( String name, boolean inherited ) + { + return originalMethod.getTagsByName( name, inherited ); + } + + public DocletTag[] getTagsByName( String name ) + { + return originalMethod.getTagsByName( name ); + } + + public TypeVariable[] getTypeParameters() + { + return originalMethod.getTypeParameters(); + } + + public int hashCode() + { + return originalMethod.hashCode(); + } + + public boolean isAbstract() + { + return originalMethod.isAbstract(); + } + + public boolean isConstructor() + { + return originalMethod.isConstructor(); + } + + public boolean isFinal() + { + return originalMethod.isFinal(); + } + + public boolean isNative() + { + return originalMethod.isNative(); + } + + public boolean isPrivate() + { + return originalMethod.isPrivate(); + } + + public boolean isPropertyAccessor() + { + return originalMethod.isPropertyAccessor(); + } + + public boolean isPropertyMutator() + { + return originalMethod.isPropertyMutator(); + } + + public boolean isProtected() + { + return originalMethod.isProtected(); + } + + public boolean isPublic() + { + return originalMethod.isPublic(); + } + + public boolean isStatic() + { + return originalMethod.isStatic(); + } + + public boolean isStrictfp() + { + return originalMethod.isStrictfp(); + } + + public boolean isSynchronized() + { + return originalMethod.isSynchronized(); + } + + public boolean isTransient() + { + return originalMethod.isTransient(); + } + + public boolean isVarArgs() + { + return originalMethod.isVarArgs(); + } + + public boolean isVolatile() + { + return originalMethod.isVolatile(); + } + + public void setAnnotations( Annotation[] annotations ) + { + originalMethod.setAnnotations( annotations ); + } + + public void setComment( String comment ) + { + originalMethod.setComment( comment ); + } + + public void setConstructor( boolean constructor ) + { + originalMethod.setConstructor( constructor ); + } + + public void setExceptions( Type[] exceptions ) + { + originalMethod.setExceptions( exceptions ); + } + + public void setLineNumber( int lineNumber ) + { + originalMethod.setLineNumber( lineNumber ); + } + + public void setModifiers( String[] modifiers ) + { + originalMethod.setModifiers( modifiers ); + } + + public void setName( String name ) + { + originalMethod.setName( name ); + } + + public void setParent( JavaClassParent parent ) + { + originalMethod.setParent( parent ); + } + + public void setParentClass( JavaClass parentClass ) + { + originalMethod.setParentClass( parentClass ); + } + + public void setReturns( Type returns ) + { + originalMethod.setReturns( returns ); + } + + public void setSourceCode( String sourceCode ) + { + originalMethod.setSourceCode( sourceCode ); + } + + public void setTags( List tagList ) + { + originalMethod.setTags( tagList ); + } + + public void setTypeParameters( TypeVariable[] typeParameters ) + { + originalMethod.setTypeParameters( typeParameters ); + } + + public boolean signatureMatches( String name, Type[] parameterTypes, boolean varArg ) + { + return originalMethod.signatureMatches( name, parameterTypes, varArg ); + } + + public boolean signatureMatches( String name, Type[] parameterTypes ) + { + return originalMethod.signatureMatches( name, parameterTypes ); + } + + public String toString() + { + return originalMethod.toString(); + } +} Property changes on: trunk/qdox/src/java/com/thoughtworks/qdox/model/JavaMethodDelegate.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:eol-style + native
Modified: trunk/qdox/src/java/com/thoughtworks/qdox/model/Type.java (725 => 726)
--- trunk/qdox/src/java/com/thoughtworks/qdox/model/Type.java 2010-03-30 16:04:01 UTC (rev 725) +++ trunk/qdox/src/java/com/thoughtworks/qdox/model/Type.java 2010-04-19 12:57:29 UTC (rev 726) @@ -350,5 +350,73 @@ return "void".equals(getValue()); } + /** + * + * @param superClass + * @return + * @since 1.12.1 + */ + protected int getTypeVariableIndex( JavaClass superClass ) { + TypeVariable[] typeVariables = superClass.getTypeParameters(); + for(int typeIndex=0;typeIndex<typeVariables.length; typeIndex++) { + if(typeVariables[typeIndex].getFullyQualifiedName().equals( getFullyQualifiedName())) { + return typeIndex; + } + } + return -1; + } + /** + * + * @param parentClass + * @return + * @since 1.12.1 + */ + protected Type resolve( JavaClass parentClass ) + { + return resolve( parentClass, parentClass ); + } + + /** + * + * @param parentClass + * @param subclass + * @return + * @since 1.12.1 + */ + protected Type resolve( JavaClass parentClass, JavaClass subclass ) + { + Type result = this; + int typeIndex = getTypeVariableIndex( parentClass ); + if ( typeIndex >= 0 ) + { + String fqn = parentClass.getFullyQualifiedName(); + if ( subclass.getSuperClass() != null && fqn.equals( subclass.getSuperClass().getFullyQualifiedName() ) ) { + result = subclass.getSuperClass().getActualTypeArguments()[typeIndex]; + } + else if ( subclass.getImplementedInterfaces() != null ) + { + for ( int i = 0; i < subclass.getImplementedInterfaces().length; i++ ) + { + if ( fqn.equals( subclass.getImplements()[i].getFullyQualifiedName() ) ) + { + result = subclass.getImplements()[i].getActualTypeArguments()[typeIndex]; + break; + } + } + } + } + + if ( this.actualArgumentTypes != null ) { + result = new Type( this.fullName, this.name, this.dimensions, this.context ); + + result.actualArgumentTypes = new Type[this.actualArgumentTypes.length]; + for (int i = 0; i < this.getActualTypeArguments().length; i++ ) + { + result.actualArgumentTypes[i] = this.actualArgumentTypes[i].resolve( parentClass, subclass ); + } + } + return result; + } + }
Modified: trunk/qdox/src/test/com/thoughtworks/qdox/GenericsTest.java (725 => 726)
--- trunk/qdox/src/test/com/thoughtworks/qdox/GenericsTest.java 2010-03-30 16:04:01 UTC (rev 725) +++ trunk/qdox/src/test/com/thoughtworks/qdox/GenericsTest.java 2010-04-19 12:57:29 UTC (rev 726) @@ -2,6 +2,8 @@ import com.thoughtworks.qdox.model.JavaClass; import com.thoughtworks.qdox.model.JavaField; +import com.thoughtworks.qdox.model.JavaMethod; + import junit.framework.TestCase; import java.io.StringReader; @@ -251,5 +253,72 @@ assertNotNull(envField); assertEquals("Map", envField.getType().getValue()); } + + // QDOX-207 + public void testMethodReturnTypeExtends() throws Exception { + String superSource = "public abstract class Test<T> {\n" + + " private T me;\n" + + " public Test(T me) {\n" + + " this.me = me;\n" + + " }\n" + + " public T getValue() {\n" + + " return me;\n" + + " }\n" + + " }"; + String subSource = "public class StringTest extends Test<String> {\n" + + " public StringTest(String s) {\n" + + " super(s);\n" + + " }\n" + + " }"; + builder.addSource( new StringReader( superSource ) ); + builder.addSource( new StringReader( subSource ) ); + JavaMethod method = builder.getClassByName( "StringTest" ).getMethodBySignature( "getValue", null, true ); + assertEquals( "T", method.getGenericReturnType().getFullyQualifiedName() ); + assertEquals( "java.lang.Object", method.getReturnType().getFullyQualifiedName() ); + assertEquals( "java.lang.Object", method.getReturnType( false ).getFullyQualifiedName() ); + assertEquals( "java.lang.String", method.getReturnType( true ).getFullyQualifiedName() ); + } + + public void testMethodReturnTypeImplements() throws Exception { + String source1="public interface GenericDao<TEntity, TKey> {\n" + + "public List<TEntity> getAll();\n" + + "public TEntity getRandom();\n" + + "public TEntity findById(TKey key);\n" + + "public TEntity persist(TEntity entity);\n" + + "public TEntity[] persist(TEntity[] entities);\n" + + "public void delete(TEntity entity);\n" + + "public Map<TKey, TEntity> asMap();" + + "}\r\n"; + String source2="public interface SubjectDao extends GenericDao<Subject, Long> {\n" + + "public List<Subject> getEnabledSubjects();\n" + + "}\r\n"; + String source3="public interface SubjectService extends RemoteService, SubjectDao {\r\n" + + "}"; + builder.addSource( new StringReader( source1 ) ); + builder.addSource( new StringReader( source2 ) ); + builder.addSource( new StringReader( source3 ) ); + JavaMethod method = builder.getClassByName( "GenericDao" ).getMethodBySignature( "getRandom", null, true ); + assertEquals( "TEntity", method.getReturnType( true ).getGenericValue() ); + method = builder.getClassByName( "GenericDao" ).getMethodBySignature( "getAll", null, true ); + assertEquals( "List<TEntity>", method.getReturnType( true ).getGenericValue() ); + method = builder.getClassByName( "GenericDao" ).getMethodBySignature( "asMap", null, true ); + assertEquals( "Map<TKey,TEntity>", method.getReturnType( true ).getGenericValue() ); + method = builder.getClassByName( "SubjectDao" ).getMethodBySignature( "getRandom", null, true ); + assertEquals( "Subject", method.getReturnType( true ).getGenericValue() ); + method = builder.getClassByName( "SubjectDao" ).getMethodBySignature( "getAll", null, true ); + assertEquals( "List<Subject>", method.getReturnType( true ).getGenericValue() ); + method = builder.getClassByName( "SubjectDao" ).getMethodBySignature( "asMap", null, true ); + assertEquals( "Map<java.lang.Long,Subject>", method.getReturnType( true ).getGenericValue() ); + + method = builder.getClassByName( "SubjectService" ).getMethodBySignature( "getRandom", null, true ); + assertEquals( "Subject", method.getReturnType( true ).getGenericValue() ); + method = builder.getClassByName( "SubjectService" ).getMethodBySignature( "getAll", null, true ); + assertEquals( "List<Subject>", method.getReturnType( true ).getGenericValue() ); + method = builder.getClassByName( "SubjectService" ).getMethodBySignature( "asMap", null, true ); + assertEquals( "Map<java.lang.Long,Subject>", method.getReturnType( true ).getGenericValue() ); + } + + + }
To unsubscribe from this list please visit:
