io.neba.api.spi.AnnotatedFieldMapper Maven / Gradle / Ivy
Show all versions of io.neba.neba-api Show documentation
/*
Copyright 2013 the original author or authors.
Licensed under the Apache License, Version 2.0 the "License";
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package io.neba.api.spi;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Map;
/**
* OSGi services implementing this interface may customize the mapping of arbitrary fields during
* the resource to model mapping of any resource model.
*
* The implementing service specifies {@link #getAnnotationType() the annotation it is responsible for}
* as well as {@link #getFieldType() the type the mapped fields} must be {@link Class#isAssignableFrom(Class) assignable to}.
*
*
* For example, the following service would be responsible for fields annotated with @MyAnnotation
* with a type assignable to {@link java.util.Collection}, e.g. List<Resource> or Set<String>.
*
*
* @Component(service = { AnnotatedFieldMapper.class })
* public class MyFieldMapper implements AnnotatedFieldMapper<Collection, MyAnnotation> {
* public Class<? extends Collection> getFieldType() {
* return Collection.class;
* }
*
* public Class<Annotation> getAnnotationType() {
* return MyAnnotation.class;
* }
*
* public Collection map(OngoingMapping<Collection, MyAnnotation> ongoingMapping) {
* ...
* }
* }
*
*
* Custom mappers are always invoked after all of NEBA's standard mappings have occurred, but before
* the corresponding value was set on the model's field. They may thus make use of the already resolved
* value or choose to provide a different one.
*
*
*
* It is crucial for a {@link AnnotatedFieldMapper} to always return a value that is assignment compatible
* to the {@link OngoingMapping#getFieldType() field type}, i.e. either of the same or of a more specific type.
* It is insufficient to return a type compatible to the {@link #map(AnnotatedFieldMapper.OngoingMapping) mapping methods}
* return type declaration. This return type only represents the type any returned value must be compatible to.
* For instance, if a mapper is responsible for {@link java.util.Collection}, it must take care to return the field's actual
* collection type, e.g. {@link java.util.List} or {@link java.util.Set}. Otherwise, an exception will arise.
*
*
*
* Implementations must never store any contextual data provided by the
* {@link AnnotatedFieldMapper.OngoingMapping}
* as this data stems from arbitrary OSGi bundles with independent life cycles.
* Storing any data would result in a class loader / memory leak when these bundles change.
*
*
* @param the super type of the {@link java.lang.reflect.Field#getType() field type}
* of the mapped type.
* @param the exact type of the annotation this mapper is responsible for.
* @author Olaf Otto
* @since 1.0.0
*/
public interface AnnotatedFieldMapper {
/**
* Represents the contextual data of a field mapping during a resource to model mapping.
*
* @param the {@link Field#getType() field type}
* @param the {@link Annotation type}
* @author Olaf Otto
*/
interface OngoingMapping {
/**
* @return The currently resolved value of the field,
* or null
if no value could be resolved for the field. This value
* has not been set to the {@link #getField() field} at this point.
*/
@CheckForNull
FieldType getResolvedValue();
/**
* @return The instance of {@link #getAnnotationType() the annotation this mapper is registered for}.
* Never null
.
*/
@Nonnull
AnnotationType getAnnotation();
/**
* @return The mapped model. At this point, the mapping is still incomplete and
* no post-processors have been invoked on the model. Never null
.
*/
@Nonnull
Object getModel();
/**
* @return the mapped field. Never null
. The field's value was not changed at this point, i.e. it is likely to
* deviate from {@link #getResolvedValue()}. Note: do not rely on this {@link java.lang.reflect.Field#getType() field's type}
* but use the {@link #getFieldType() provided field type} instead, as these types may be different, for instance in case
* of {@link io.neba.api.resourcemodels.Lazy} fields.
*/
@Nonnull
Field getField();
/**
* @return All annotations (including meta-annotations, i.e. annotations of annotations) present on the field.
* Never null
.
*/
@Nonnull
Map, Annotation> getAnnotationsOfField();
/**
* @return The mapped type of the field. Note: In case the field is {@link io.neba.api.resourcemodels.Lazy}, this
* type is the component type, i.e. the type targeted by the optional field. However, field mappers
* are not applied optional fields but to the subsequent mapping, when the {@link io.neba.api.resourcemodels.Lazy#get() lazy value is actually mapped}.
* Never null
.
*/
@Nonnull
Class> getFieldType();
/**
* @return the generic type parameter of the {@link #getFieldType() field type}, or null
if
* no such parameter exists.
*/
@CheckForNull
Class> getFieldTypeParameter();
/**
* @return the repository path that shall be resolved to the field's value, as determined by the
* field name or {@link io.neba.api.annotations.Path path annotation}. Placeholders
* in the path are resolved at this point. Never null
.
*/
@Nonnull
String getRepositoryPath();
/**
* @return The resource that is mapped to the model. Never null
, but may be a synthetic resource.
*/
@Nonnull
Resource getResource();
/**
* @return The {@link org.apache.sling.api.resource.ValueMap} representation of the {@link #getResource() resource}.
* This value map does support primitive types, e.g. int
. May be null
if the resource
* has no properties, e.g. if it is synthetic.
*/
@CheckForNull
ValueMap getProperties();
}
/**
* @return never null
.
*/
@Nonnull
Class super FieldType> getFieldType();
/**
* @return never null
.
*/
@Nonnull
Class getAnnotationType();
/**
* @param ongoingMapping never null
.
* @return The value to be set on the mapped field during the resource to model mapping.
* Must return a value that is assignment-compatible to {@link OngoingMapping#getFieldType()}.
* Can be null
.
*/
@CheckForNull
FieldType map(OngoingMapping ongoingMapping);
}