Review request for type annotation reflection test

Charlie Wang charlie.wang at oracle.com
Fri Sep 6 00:11:51 PDT 2013


Hi Sergey,
   I've updated my code according to your comments. Please take a look 
again.
http://cr.openjdk.java.net/~pzhang/Charlie/TypeAnnotation1/webrev/

P.S. When updating Helper.isPkgInfoPresent with: return 
files.stream().map(JavaFileObject::getName).anyMatch(name -> 
name.contains("package-info") || name.contains("PkgAnno"));
There is a compile error. So the code stays the original way. Due to the 
urgency of the test, I will file a RFE bug to track it and update the 
piece of code when time allows.



- Charlie


On 2013/8/27 2:27, Sergey Grinev wrote:
> Hi Charlie,
>
> please, take a look at few inline comments.
>
> -- Sergey
>
> On 26-Aug-13 11:45, Charlie Wang wrote:
>> Hi Alex,
>>   I think I will need to make update according to Sergey's comments 
>> before check in the code.
>>
>> Sergey,
>>   Thanks for the comments. My reply inline.
>>
>> On 2013/8/23 18:36, Sergey Grinev wrote:
>>> Hi, all.
>>>
>>> Sorry if this review covers same issue I reported on reviewboard 
>>> one, but it seems few important point were not fixed:
>>>
>>> 1.  public Map<Helper.Declaration, Object> lhm = new LinkedHashMap<>();
>>> and
>>>     public synchronized static String genDeclaration(
>>>             Map<Helper.Declaration, Object> input, Declaration... 
>>> srcType)
>>>
>>> - fields used as a method-local temporary variables are error-prone 
>>> and confusing. Users/readers of that code are not aware when this 
>>> field can be used or when this field can be cleared, also using such 
>>> field is not thread-safe. Imagine someone added one more ENUM value 
>>> to TestCase, used lhm and didn't call lhm.clear() -- things will 
>>> break and very different place this person didn't even touch.
>>>
>>> - Map claims it stores Objects but actually they are only String and 
>>> String[]. Thus your getDeclaration() has invalid signature and is 
>>> error-prone. Also you calls instanceof several times to resolve this 
>>> ambiguity which is considered to be an anti-pattern as well.
>>>
>>> You can solve these problems by introducing separate entity 
>>> responsible for declaration generation:
>>>
>>> public class DeclarationGenerator {
>>>
>>>     private final Map<Helper.Declaration, String[]> lhm = new 
>>> LinkedHashMap<>();
>>>         public void add(Helper.Declaration key, String... value) {
>>>         lhm.put(key, value);
>>>     }
>>>     public String genDeclaration(Declaration... srcType) {
>>>         //...
>>>         for (Declaration st : Declaration.values()) {
>>>                 if (input.containsKey(st))
>>> result.append(st.getSrc(input.get(st)));
>>>         }
>>>         //...
>>>     }
>>> }
>>>
>>> with this helper class instead of
>>>
>>>                 lhm.clear();
>>>                 lhm.put(Helper.Declaration.IMPORT, null);
>>>                 lhm.put(Helper.Declaration.CLASS, str[0]);
>>>                 testCode += Helper.genDeclaration(lhm);
>>>
>>> you can write
>>>
>>>                 DeclarationGenerator dg = new 
>>> DeclarationGenerator(); // not to reuse, create new one for each 
>>> generation
>>>                 dg.put(Helper.Declaration.IMPORT, null);
>>>                 dg.put(Helper.Declaration.CLASS, str[0]);
>>>                 testCode += dg.genDeclaration();
>>>
>>> having clean design without field used as temporary variable and 
>>> type ambiguity.
>>>
>> I agree with you. I will update the code accordingly.
>>
>>>
>>> 2. several TestCase enum entries, especially TESTBASECLSSTART and 
>>> TESTBASECLS are copypasted in several places.
>> I will remove the duplications.
>
> You may want to consider using separate enum for all your cases (or 
> one per package).
>
>>
>>>
>>> 3. public static Map<String, String> testInput = new LinkedHashMap<>();
>>>
>>> why this field in static?
>>>
>> Made it static for enum methods to access directly.
>
> Oh I missed usages of this field, sorry. Current approach isn't the 
> best from OOD point of view. You have static field cleaned in 
> constructor and field being reused from the code outside of the 
> AnnotationTest. This leads to uncontrollable logic flow and 
> impossibility of parallel invocation.
>
> The convenient approach would be
> 1) either having this field non-static and provide instance of 
> AnnotationTest or testInput as parameter to each genTestCase() call.
> 2) or using anonymous classes (or even lambdas) instead of enum, which 
> allows convenient access to "this" without statics:
>
>     private final static TestCaseGenerator TESTBASECLSSTART1 = (String 
> str) -> {
>         testInput.put(null, null); // testInput not static
>
>         // other code
>
>         return "";
>     };
>
> P.S.: AnnotationTest.GenTestCode() method should be *g*enTestCode() by 
> common Java code guidelines.
>
>>
>>
>> And as to the other comments from you:
>>
>>> 1. Helper#getAnno()
>>>
>>>   StringBuffer result = new StringBuffer("");
>>>
>>> using StringBuilder is recommended for thread-safe methods
>> Agree.
>>
>>>
>>> 2. List Helper.getMultipleAT()
>>>
>>> this method uses List where List<String> can be specified
>> Good check.
>>
>>>
>>> 3. Helper.compareAnnoWithDiffSeq()
>>>
>>> this can be done much shorted and without copying array to list:
>>>
>>>         String[] split1 = anno1.trim().split("\\s+");
>>>         String[] split2 = anno2.trim().split("\\s+");
>>>         Arrays.sort(split1);
>>>         Arrays.sort(split2);
>>>         return Arrays.equals(split1, split2);
>> OK.
>>
>>>
>>> 4. Helper.compileCode():697-704 can be simplified using jdk8 streams:
>>>
>>> ok = diagnostics.getDiagnostics().stream().anyMatch(d -> d.getKind() 
>>> == Diagnostic.Kind.ERROR);
>> I didn't adopt it because I wasn't sure if I supposed to use this new 
>> jdk8 feature in this jdk8 test. Bus since Tristan also suggested it 
>> is doable, I will update my code this way.
>>
>>>
>>> 5. Same for Helper.isPkgInfoPresent:
>>>
>>> return files.stream().map(JavaFileObject::getName).anyMatch(name -> 
>>> name.contains("package-info") || name.contains("PkgAnno"));
>> OK.
>>
>>>
>>> 6. AnnotatedArrayTypeGetAnnotatedGenericComponentTypeTest:175
>>>
>>> You are calling Helper.getArrAT(at) then split it by ";" and work 
>>> with result.
>>> while Helper.getArrAT() takes a list and convert it into string:
>>> return getAnno(annotations) + ";" + ret;
>>>
>>> It may be more efficient to work directly with lists.
>> The expected results (to be compared with test results) also come as 
>> strings because they are directly extracted from the test input, 
>> which is why I made it "getAnno(annotations) + ";" + ret" - so I 
>> don't have to split the result into lists. Either way, the conversion 
>> has to be made. The present way is more direct.
>>
>>>
>>> 7. DeclarationGenerator interface is never used as interface
>> I don't think it hurts to put an interface there. At least it will 
>> help extension.
> Agree.
>>
>>>
>>> 8. Helper.Declaration.replaceAnnoStr should be static
>> Isn't it static already.
>
>     http://cr.openjdk.java.net/~pzhang/Charlie/TypeAnnotation1/webrev/raw_files/new/test/type-annotations/Helper.java
>     <http://cr.openjdk.java.net/%7Epzhang/Charlie/TypeAnnotation1/webrev/raw_files/new/test/type-annotations/Helper.java>
>
>     private String[] replaceAnnoStr(String ss, String temp,
>
>
> Seems no.
>>
>>>
>>> 9. Helper.Declaration.replaceAnnoStr/getFirstAnno return result as 
>>> String[] array, which is considered to be a bad practice (you are 
>>> using variable length structure to store exactly 2 values). It would 
>>> be cleaner to introduce intermediate structure of 2 string to store 
>>> return value.
>> Then I will have to declare a new class for the two strings.
>
> But that's the point. It would be class with fields with meaningful 
> names instead of [0] and [1]. Anyone who will be 
> supporting/fixing/using your code in future would be grateful for 
> that. And you will avoid using array to store fixed length data which 
> considered to be a bad practice.
>
> Thanks,
>
> Sergey.
>
>>
>>
>> I will update my code according to most of your comments. Could you 
>> please reply to me asap if you think there's anything missing. 
>> Hopefully we can wrap things up within the week.
>>
>>
>>
>> - Charlie
>>
>>
>>>
>>> On 22-Aug-13 22:43, Alex Buckley wrote:
>>>> I have no further comments. Thanks for all your work to test this 
>>>> new and expansive API.
>>>>
>>>> Alex
>>>>
>>>> On 8/20/2013 1:56 AM, Charlie Wang wrote:
>>>>> Hi Alex,
>>>>>    I used testInput.size() instead of static int clsIdx as suffix 
>>>>> this
>>>>> time. And other update according to your comments.
>>>>>
>>>>> Here's the link. Please review again.
>>>>> http://cr.openjdk.java.net/~pzhang/Charlie/TypeAnnotation1/webrev/
>>>>>
>>>>>
>>>>> - Charlie
>>>>>
>>>>> On 2013/8/9 1:05, Alex Buckley wrote:
>>>>>> - AnnotationTest.i/clsIdx: it wasn't the name of the variable I was
>>>>>> objecting to. It was the fact that you're relying on a mutable 
>>>>>> public
>>>>>> static variable at all.
>>>>>>
>>>>>> - GenTestCode should take Class<? extends TestCaseGenerator>, so you
>>>>>> don't need to cast to TestCaseGenerator in GenTestCode. (Same 
>>>>>> comment
>>>>>> for AnnotationTest.compileCode).
>>>>>>
>>>>>> - AnnotationTest.testInput is a Map<Object,String> but I'm sure you
>>>>>> can do better than Object for keys.'
>>>>>>
>>>>>> - ExecutableGetAnnotatedParameterTypesTest.testInput hides
>>>>>> AnnotationTest.testInput, but swaps the order of the type arguments!
>>>>>> Map<String,Object> is either wrong, or should not use Object.
>>>>>>
>>>>>> Alex
>>>
>>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/type-annotations-dev/attachments/20130906/47e25645/attachment.html 


More information about the type-annotations-dev mailing list