122 public interface Annotator { |
135 public interface Annotator { |
123 void enterAnnotation(); |
136 void enterAnnotation(); |
124 String toString(); |
137 String toString(); |
125 } |
138 } |
126 |
139 |
|
140 /** |
|
141 * This context contains all the information needed to synthesize new |
|
142 * annotations trees by the completer for repeating annotations. |
|
143 */ |
|
144 public class AnnotateRepeatedContext { |
|
145 public final Env<AttrContext> env; |
|
146 public final Map<Symbol.TypeSymbol, ListBuffer<Attribute.Compound>> annotated; |
|
147 public final Map<Attribute.Compound, JCDiagnostic.DiagnosticPosition> pos; |
|
148 public final Log log; |
|
149 |
|
150 public AnnotateRepeatedContext(Env<AttrContext> env, |
|
151 Map<Symbol.TypeSymbol, ListBuffer<Attribute.Compound>> annotated, |
|
152 Map<Attribute.Compound, JCDiagnostic.DiagnosticPosition> pos, |
|
153 Log log) { |
|
154 Objects.requireNonNull(env); |
|
155 Objects.requireNonNull(annotated); |
|
156 Objects.requireNonNull(pos); |
|
157 Objects.requireNonNull(log); |
|
158 |
|
159 this.env = env; |
|
160 this.annotated = annotated; |
|
161 this.pos = pos; |
|
162 this.log = log; |
|
163 } |
|
164 |
|
165 /** |
|
166 * Process a list of repeating annotations returning a new |
|
167 * Attribute.Compound that is the attribute for the synthesized tree |
|
168 * for the container. |
|
169 * |
|
170 * @param repeatingAnnotations a List of repeating annotations |
|
171 * @return a new Attribute.Compound that is the container for the repeatingAnnotations |
|
172 */ |
|
173 public Attribute.Compound processRepeatedAnnotations(List<Attribute.Compound> repeatingAnnotations) { |
|
174 return Annotate.this.processRepeatedAnnotations(repeatingAnnotations, this); |
|
175 } |
|
176 |
|
177 /** |
|
178 * Queue the Annotator a on the repeating annotations queue of the |
|
179 * Annotate instance this context belongs to. |
|
180 * |
|
181 * @param a the Annotator to enqueue for repeating annotation annotating |
|
182 */ |
|
183 public void annotateRepeated(Annotator a) { |
|
184 Annotate.this.repeated(a); |
|
185 } |
|
186 } |
127 |
187 |
128 /* ******************************************************************** |
188 /* ******************************************************************** |
129 * Compute an attribute from its annotation. |
189 * Compute an attribute from its annotation. |
130 *********************************************************************/ |
190 *********************************************************************/ |
131 |
191 |
266 } |
326 } |
267 if (!expected.isErroneous()) |
327 if (!expected.isErroneous()) |
268 log.error(tree.pos(), "annotation.value.not.allowable.type"); |
328 log.error(tree.pos(), "annotation.value.not.allowable.type"); |
269 return new Attribute.Error(attr.attribExpr(tree, env, expected)); |
329 return new Attribute.Error(attr.attribExpr(tree, env, expected)); |
270 } |
330 } |
|
331 |
|
332 /* ********************************* |
|
333 * Support for repeating annotations |
|
334 ***********************************/ |
|
335 |
|
336 /* Process repeated annotations. This method returns the |
|
337 * synthesized container annotation or null IFF all repeating |
|
338 * annotation are invalid. This method reports errors/warnings. |
|
339 */ |
|
340 private Attribute.Compound processRepeatedAnnotations(List<Attribute.Compound> annotations, |
|
341 AnnotateRepeatedContext ctx) { |
|
342 Attribute.Compound firstOccurrence = annotations.head; |
|
343 List<Attribute> repeated = List.nil(); |
|
344 Type origAnnoType; |
|
345 Type arrayOfOrigAnnoType = null; |
|
346 Type targetContainerType = null; |
|
347 MethodSymbol containerValueSymbol = null; |
|
348 |
|
349 Assert.check(!annotations.isEmpty() && |
|
350 !annotations.tail.isEmpty()); // i.e. size() > 1 |
|
351 |
|
352 for (List<Attribute.Compound> al = annotations; |
|
353 !al.isEmpty(); |
|
354 al = al.tail) |
|
355 { |
|
356 Attribute.Compound currentAnno = al.head; |
|
357 |
|
358 origAnnoType = currentAnno.type; |
|
359 if (arrayOfOrigAnnoType == null) { |
|
360 arrayOfOrigAnnoType = types.makeArrayType(origAnnoType); |
271 } |
361 } |
|
362 |
|
363 Type currentContainerType = getContainingType(currentAnno, ctx.pos.get(currentAnno)); |
|
364 if (currentContainerType == null) { |
|
365 continue; |
|
366 } |
|
367 // Assert that the target Container is == for all repeated |
|
368 // annos of the same annotation type, the types should |
|
369 // come from the same Symbol, i.e. be '==' |
|
370 Assert.check(targetContainerType == null || currentContainerType == targetContainerType); |
|
371 targetContainerType = currentContainerType; |
|
372 |
|
373 containerValueSymbol = validateContainer(targetContainerType, origAnnoType, ctx.pos.get(currentAnno)); |
|
374 |
|
375 if (containerValueSymbol == null) { // Check of CA type failed |
|
376 // errors are already reported |
|
377 continue; |
|
378 } |
|
379 |
|
380 repeated = repeated.prepend(currentAnno); |
|
381 } |
|
382 |
|
383 if (!repeated.isEmpty()) { |
|
384 repeated = repeated.reverse(); |
|
385 JCAnnotation annoTree; |
|
386 TreeMaker m = make.at(ctx.pos.get(firstOccurrence)); |
|
387 Pair<MethodSymbol, Attribute> p = |
|
388 new Pair<MethodSymbol, Attribute>(containerValueSymbol, |
|
389 new Attribute.Array(arrayOfOrigAnnoType, repeated)); |
|
390 annoTree = m.Annotation(new Attribute.Compound(targetContainerType, |
|
391 List.of(p))); |
|
392 Attribute.Compound c = enterAnnotation(annoTree, |
|
393 targetContainerType, |
|
394 ctx.env); |
|
395 return c; |
|
396 } else { |
|
397 return null; // errors should have been reported elsewhere |
|
398 } |
|
399 } |
|
400 |
|
401 /** Fetches the actual Type that should be the containing annotation. */ |
|
402 private Type getContainingType(Attribute.Compound currentAnno, |
|
403 DiagnosticPosition pos) |
|
404 { |
|
405 Type origAnnoType = currentAnno.type; |
|
406 TypeSymbol origAnnoDecl = origAnnoType.tsym; |
|
407 |
|
408 // Fetch the ContainedBy annotation from the current |
|
409 // annotation's declaration, or null if it has none |
|
410 Attribute.Compound ca = origAnnoDecl.attribute(syms.containedByType.tsym); |
|
411 if (ca == null) { // has no ContainedBy annotation |
|
412 log.error(pos, "duplicate.annotation.missing.container", origAnnoType); |
|
413 return null; |
|
414 } |
|
415 |
|
416 return filterSame(extractContainingType(ca, pos, origAnnoDecl), |
|
417 origAnnoType); |
|
418 } |
|
419 |
|
420 // returns null if t is same as 's', returns 't' otherwise |
|
421 private Type filterSame(Type t, Type s) { |
|
422 if (t == null || s == null) { |
|
423 return t; |
|
424 } |
|
425 |
|
426 return types.isSameType(t, s) ? null : t; |
|
427 } |
|
428 |
|
429 /** Extract the actual Type to be used for a containing annotation. */ |
|
430 private Type extractContainingType(Attribute.Compound ca, |
|
431 DiagnosticPosition pos, |
|
432 TypeSymbol annoDecl) |
|
433 { |
|
434 // The next three checks check that the ContainedBy annotation |
|
435 // on the declaration of the annotation type that is repeating is |
|
436 // valid. |
|
437 |
|
438 // ContainedBy must have at least one element |
|
439 if (ca.values.isEmpty()) { |
|
440 log.error(pos, "invalid.containedby.annotation", annoDecl); |
|
441 return null; |
|
442 } |
|
443 Pair<MethodSymbol,Attribute> p = ca.values.head; |
|
444 Name name = p.fst.name; |
|
445 if (name != names.value) { // should contain only one element, named "value" |
|
446 log.error(pos, "invalid.containedby.annotation", annoDecl); |
|
447 return null; |
|
448 } |
|
449 if (!(p.snd instanceof Attribute.Class)) { // check that the value of "value" is an Attribute.Class |
|
450 log.error(pos, "invalid.containedby.annotation", annoDecl); |
|
451 return null; |
|
452 } |
|
453 |
|
454 return ((Attribute.Class)p.snd).getValue(); |
|
455 } |
|
456 |
|
457 /* Validate that the suggested targetContainerType Type is a valid |
|
458 * container type for repeated instances of originalAnnoType |
|
459 * annotations. Return null and report errors if this is not the |
|
460 * case, return the MethodSymbol of the value element in |
|
461 * targetContainerType if it is suitable (this is needed to |
|
462 * synthesize the container). */ |
|
463 private MethodSymbol validateContainer(Type targetContainerType, |
|
464 Type originalAnnoType, |
|
465 DiagnosticPosition pos) { |
|
466 MethodSymbol containerValueSymbol = null; |
|
467 boolean fatalError = false; |
|
468 |
|
469 // Validate that there is a (and only 1) value method |
|
470 Scope scope = targetContainerType.tsym.members(); |
|
471 int nr_value_elems = 0; |
|
472 boolean error = false; |
|
473 for(Symbol elm : scope.getElementsByName(names.value)) { |
|
474 nr_value_elems++; |
|
475 |
|
476 if (nr_value_elems == 1 && |
|
477 elm.kind == Kinds.MTH) { |
|
478 containerValueSymbol = (MethodSymbol)elm; |
|
479 } else { |
|
480 error = true; |
|
481 } |
|
482 } |
|
483 if (error) { |
|
484 log.error(pos, |
|
485 "invalid.containedby.annotation.multiple.values", |
|
486 targetContainerType, |
|
487 nr_value_elems); |
|
488 return null; |
|
489 } else if (nr_value_elems == 0) { |
|
490 log.error(pos, |
|
491 "invalid.containedby.annotation.no.value", |
|
492 targetContainerType); |
|
493 return null; |
|
494 } |
|
495 |
|
496 // validate that the 'value' element is a method |
|
497 // probably "impossible" to fail this |
|
498 if (containerValueSymbol.kind != Kinds.MTH) { |
|
499 log.error(pos, |
|
500 "invalid.containedby.annotation.invalid.value", |
|
501 targetContainerType); |
|
502 fatalError = true; |
|
503 } |
|
504 |
|
505 // validate that the 'value' element has the correct return type |
|
506 // i.e. array of original anno |
|
507 Type valueRetType = containerValueSymbol.type.getReturnType(); |
|
508 Type expectedType = types.makeArrayType(originalAnnoType); |
|
509 if (!(types.isArray(valueRetType) && |
|
510 types.isSameType(expectedType, valueRetType))) { |
|
511 log.error(pos, |
|
512 "invalid.containedby.annotation.value.return", |
|
513 targetContainerType, |
|
514 valueRetType, |
|
515 expectedType); |
|
516 fatalError = true; |
|
517 } |
|
518 |
|
519 // validate that all other elements of containing type has defaults |
|
520 scope = targetContainerType.tsym.members(); |
|
521 error = false; |
|
522 for(Symbol elm : scope.getElements()) { |
|
523 if (elm.name != names.value && |
|
524 elm.kind == Kinds.MTH && |
|
525 ((MethodSymbol)elm).defaultValue == null) { |
|
526 log.error(pos, |
|
527 "invalid.containedby.annotation.elem.nondefault", |
|
528 targetContainerType, |
|
529 elm); |
|
530 containerValueSymbol = null; |
|
531 error = true; |
|
532 } |
|
533 } |
|
534 if (error) { |
|
535 fatalError = true; |
|
536 } |
|
537 |
|
538 // Explicitly no check for/validity of @ContainerFor. That is |
|
539 // done on declaration of the container, and at reflect time. |
|
540 |
|
541 // The rest of the conditions for a valid containing annotation are made |
|
542 // in Check.validateRepeatedAnnotaton(); |
|
543 |
|
544 return fatalError ? null : containerValueSymbol; |
|
545 } |
|
546 } |