Skip to content

Instantly share code, notes, and snippets.

@simonthum
Created December 6, 2012 21:47
Show Gist options
  • Save simonthum/4228792 to your computer and use it in GitHub Desktop.
Save simonthum/4228792 to your computer and use it in GitHub Desktop.
Detatched Annoations for Celyon
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