io.neba.api.resourcemodels.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.resourcemodels;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
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>.
*
*
* @Service
* @Component(immediate = true)
* public class MyFieldMapper<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 io.neba.api.resourcemodels.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
*/
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.
*/
FieldType getResolvedValue();
/**
* @return The instance of {@link #getAnnotationType() the annotation this mapper is registered for}.
* Never null
.
*/
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
.
*/
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.Optional} fields.
*/
Field getField();
/**
* @return All annotations (including meta-annotations, i.e. annotations of annotations) present on the field.
* Never null
.
*/
Map, Annotation> getAnnotationsOfField();
/**
* @return The mapped type of the field. Note: In case the field is {@link io.neba.api.resourcemodels.Optional}, 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 Optional#get() optional value is actually mapped.
* Never null
.
*/
Class> getFieldType();
/**
* @return the generic type parameter of the {@link #getFieldType() field type}, or null
if
* no such parameter exists.
*/
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
.
*/
String getRepositoryPath();
/**
* @return The resource that is mapped to the model. Never null
, but may be a synthetic resource.
*/
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. {@link int.class}. May be null
if the resource
* has no properties, e.g. if it is synthetic.
*/
ValueMap getProperties();
}
/**
* @return never null
.
*/
Class super FieldType> getFieldType();
/**
* @return never null
.
*/
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
.
*/
FieldType map(OngoingMapping ongoingMapping);
}