Created
December 6, 2012 21:47
-
-
Save simonthum/4228792 to your computer and use it in GitHub Desktop.
Detatched Annoations for Celyon
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
doc "An annotation context defines a context in which \"detached\" | |
annotations are associated to annotable program elements. | |
Annotations are detached if they are not defined in the source code of | |
the corresponding program element. | |
The annotation context makes it possible to view source-code annotations and detached | |
annotations as equals. It thus enables to use annotations in cases that traditionally | |
required switching to other (less type-checked) means of configuration. | |
The annotation context interface allows program element related configuration | |
to be implemented as a context, thus giving the programmer the ability | |
to alter, enhance or even discard the conventions and configuration of | |
compliant libraries and frameworks. Also, it enables to use annotations | |
to configure 3rd-party code to work with local or 4th-party code, thereby | |
making annotations a first-class software configuration mechanism frameworks | |
can rely upon. | |
A compliant library or framework merely has to define the basic configuration | |
annotations it understands and a way to negotiate the context(s) to use. | |
Its configuration can then be expressed in any form the programmer chooses, | |
including but not limited to framework-provided mechanisms, custom conventions, | |
config files, compile-time checked metamodel-reference-to-annotation associations, | |
or transformed annotations from the source or other contexts. | |
From a platform perspective, this has several benfits: | |
- It establishes a clear preferred way of handling type-related configuration | |
- It softens the sharp degradation in useability occurring when annotations | |
don't work and are replaced by config files | |
- It relieves CoC frameworks from pursuing one-size-fits-all conventions | |
- It enables type-safe configuration across unrelated or disparate code, | |
making distributed or unrelated teams more effective" | |
by("Simon Thum") | |
shared interface AnnotationContext | |
satisfies Identifiable & Correspondence<Annotated, Sequence<Annotation<Bottom>>> { | |
doc "The parent context(s). This is a collection of annotation contexts this | |
context may legitimately query for their annotations. Only direct dependencies | |
are listed, cycles are prohibited." | |
shared formal AnnotationContext[] parents; | |
doc "Unlike item, the get operation may incorporate annotations from other | |
contexts. How this might be done is left unspecified." | |
shared formal Annotation<Bottom>[] get(Annotated programElement); | |
} | |
doc "A validating annotation context is able to provide guarantees about | |
the returned annotations, independent of the effective context. The caller | |
has to make sure validation actually happens, however. See the global | |
annotations function. | |
Implementations should be precise about the purpose and semantics | |
of the validation. Actually, they should also be certain that the | |
purpose is worth the effort and cost associated with validating | |
end results, for example to meet security guarantees." | |
shared interface ValidatingAnnotationContext | |
satisfies AnnotationContext { | |
doc "validates a result obtained from a child context and throws if invalid." | |
throws(InvalidAnnotationContextException, "if the result does not meet validation criteria") | |
shared formal void validate(Annotated element, Annotation<Bottom>[] result); | |
} | |
doc "This abstract base ensures that the context does not depend on itself over indirect dependencies. | |
There are no cycles in the annotation context graph if all contexts adhere to this constraint." | |
shared abstract class AbstractAnnotationContext(parents) satisfies AnnotationContext { | |
void searchThis(AnnotationContext context) { | |
for (p in context.parents) { | |
if (p === this) { | |
throw Exception("cycle in parent annotation contexts detected"); | |
} | |
searchThis(p); | |
} | |
} | |
searchThis(this); // another reason for a way around the this restriction? | |
shared actual AnnotationContext[] parents; | |
} | |
doc "This abstract base ensures that the context is not modified after it | |
has been read for the first time or finished. Basically it tries | |
not to be too dynamic, annotations are as long-lived as the context. | |
TODO this subclassing approach seems bankrupt." | |
shared abstract class AbstractMutableAnnotationContext(AnnotationContext[] parents) extends AbstractAnnotationContext(parents) { | |
variable Boolean _finished := false; | |
Boolean finished { | |
return _finished; | |
} | |
assign finished { | |
if (finished != _finished && _finished == true) { | |
throw Exception(); | |
} | |
} | |
void changeContext() { | |
if (finished) { | |
throw Exception("A finished annotation context cannot be altered."); | |
} | |
} | |
} | |
doc "The ceylon source annotation context. It looks up ceylon source code annotations and | |
makes sure none of them are removed in inferior contexts iff a validing query is performed | |
using the global annotations getter. Whether the global annotations getter implementation | |
uses this context or vice versa is left unspecified." | |
shared object ceylonSourceAnnotationContext satisfies ValidatingAnnotationContext { | |
shared actual Annotation<Bottom>[] get(Annotated programElement) { | |
// TODO return source code annotations | |
return bottom; | |
} | |
shared actual Sequence<Annotation<Bottom>>? item(Annotated key) { | |
// because we are the root context, these two are interchangeable | |
value ctx = get(key); | |
if (nonempty ctx) { | |
return ctx; | |
} else { | |
return null; | |
} | |
} | |
shared actual void validate(Annotated element, Annotation<Bottom>[] result) { | |
// might be relaxed to ceylon.language or subclasses thereof? | |
if (!result.containsEvery( get(element) )) { | |
throw InvalidAnnotationContextException("ceylon source annotations have been removed"); | |
} | |
// probably we should also replicate compile-time constraints (optional/sequenced) | |
// as run-time constraints. | |
} | |
shared actual AnnotationContext[] parents = empty; | |
} | |
doc "This annotation context can be populated at run-time. It is intended to be the workhorse | |
annotation context for modifying and adding annotations when source annotations fail. | |
Use cases: | |
context.addTo(Class.@member, serializable()); | |
context.removeFrom(Class.@otherMember, serializable()); | |
... | |
serializer.useAnnotationsFrom(context);" | |
shared class RefiningAnnotationContext( | |
doc "This context may have at most one parent." AnnotationContext[] parents) | |
extends AbstractMutableAnnotationContext(parents) { | |
if (parents.count > 1) { // can't make much sense of this error | |
throw Exception(); | |
} | |
AnnotationContext? parent = parents[0]; | |
shared actual Annotation<Bottom>[] get(Annotated programElement) { | |
// get parent annotatiions if any | |
Annotation<Bottom>[] p_ann; | |
if (exists parent) { | |
p_ann = parent.get(programElement); | |
} else { | |
p_ann = empty; | |
} | |
// remove some annot. from parent (special list, probably not much needed) | |
// add own annotations and replace exiting ones (main functionality) | |
// upholding compile-time contraints would be cool | |
return bottom; | |
} | |
shared actual Sequence<Annotation<Bottom>>? item(Annotated key) { | |
// return own annotations | |
return bottom; | |
} | |
} | |
doc "thrown when a validatng annotation context fails to validate result annotations" | |
shared class InvalidAnnotationContextException | |
( String? description=null, Exception? cause=null ) | |
extends Exception(description, cause) {} | |
doc "The global detached annotation accessor function." | |
shared Annotation<Bottom>[] contextAnnotations<Value, ProgramElement>( | |
Type<ConstrainedAnnotation<Value, Bottom, ProgramElement>> annotationType, | |
ProgramElement programElement, AnnotationContext context = ceylonSourceAnnotationContext) | |
given Value satisfies ConstrainedAnnotation<Value, Bottom, ProgramElement> | |
// given Values satisfies Empty|Sequence<Annotation<Value>> | |
given ProgramElement satisfies Annotated { | |
// query the context | |
value result = context.get(programElement); | |
// validate the final result by all parent contexts. If we directly | |
// queried a root context, optimize the self-validation. | |
if (is ValidatingAnnotationContext context && nonempty context.parents) { | |
void validate(ProgramElement programElement, Annotation<Bottom>[] result, AnnotationContext context) { | |
// validate parent contexts | |
for (c in context.parents) { | |
validate(programElement, result, context); | |
} | |
// validate this | |
if (is ValidatingAnnotationContext context) { | |
context.validate(programElement, result); | |
} | |
} | |
validate(programElement, result, context); | |
} | |
// narrowing at last - can't be done earlier as contexts should be | |
// unconstrained by query details. | |
// value narrowed_res = for(a in result) if (is \IannotationType a) a; | |
return result; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment