/**
* 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.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
/**
* Marks a field as containing a reference, i.e. a path to another resource.
* Example: The property "searchPage" contains the path
* "/content/mysite/de/search" (a String value). without the @{@link Reference} annotation,
* the property could be mapped like so:
*
* <p>
*
* <pre>
* @{@link ResourceModel}("my/resource/type")
* public class MyModel {
* private String searchPage;
* ...
* }
* </pre>
*
* </p>
*
* However, if one wants to obtain a model representing the referenced search
* page, e.g. "SearchPage", one would have to obtain a
* {@link org.apache.sling.api.resource.ResourceResolver}, get the
* {@link org.apache.sling.api.resource.Resource} using the "searchPage"
* property value, check for null and finally
* {@link org.apache.sling.api.resource.Resource#adaptTo(Class) adapt} the
* {@link org.apache.sling.api.resource.Resource} to the "SearchPage" model:
*
* <p>
*
* <pre>
* @{@link ResourceModel}("my/resource/type")
* public class MyModel {
* private String searchPage;
* @{@link This}
* private Resource resource;
*
* public SearchPage getSearchPage() {
* SearchPage page = null;
* if (!isBlank(this.searchPage)) {
* Resource pageResource = this.resource.getResourceResolver().get(this.searchPage);
* if (pageResource != null) {
* page = pageResource.adaptTo(SearchPage.class);
* }
* }
* return page;
* }
* }
* </pre>
*
* </p>
*
* This boilerplate code is no longer necessary when using the @{@link Reference} annotation:
*
* <p>
*
* <pre>
* @{@link ResourceModel}("my/resource/type")
* public class MyModel {
* @{@link Reference}
* private SearchPage searchPage;
* }
* </pre>
*
* </p>
*
* This will automatically
* {@link org.apache.sling.api.resource.ResourceResolver#getResource(String)
* get} the {@link org.apache.sling.api.resource.Resource} denoted by the string
* value "searchPage" and
* {@link org.apache.sling.api.resource.Resource#adaptTo(Class) adapt it} to the
* "SearchPage" model. One can also use this annotation in conjunction with the @
* {@link Path} annotation:
* <p>
*
* <pre>
* ...
* @{@link Path}("some:searchPagePath")
* @{@link Reference}
* private SearchPage searchPage;
* </pre>
*
* </p>
* However, {@link Reference} is unnecessary if {@link Path} is absolute, since
* an absolute path is always considered a reference to a
* {@link org.apache.sling.api.resource.Resource}, thus the following works:
* <p>
*
* <pre>
* ...
* @{@link Path}("/content/mysite/${language}/search")
* private SearchPage searchPage;
* </pre>
*
* </p>
*
* {@link java.util.Collection}, {@link java.util.List}, {@link java.util.Set}
* and arrays of references are also supported. In this case, the corresponding
* property ("pages" in the example below) must have the type String[].
*
* <p>
*
* <pre>
* ...
* @{@link Reference}
* private List<Page> pages;
* </pre>
*
* </p>
*
* Note that upper bound generic types are not supported since the corresponding
* collection would be read-only. Thus, the following does not work:
*
* <p>
*
* <pre>
* ...
* @{@link Reference}
* private List<? extends Page> pages;
* </pre>
*
* </p>
*
* However, lower bound generic types are supported. Thus, the following does
* work:
*
* <p>
*
* <pre>
* ...
* @{@link Reference}
* private List<? super Page> pages;
* </pre>
*
* </p>
*
* In case you want to alter the reference prior to resolution, e.g. to obtain specific children of the referenced resource,
* you can modify the reference path(s) by providing a path segment that is appended to all reference paths, like so:
*
* <p>
*
* <pre>
* ...
* @{@link Reference}(append = "jcr:content")
* private List<PageContent> pageContents;
* </pre>
*
* </p>
*
* Thus, if the reference(s) point to pages, e.g. /content/page/a, /jcr:content would be appended to the reference path, resulting in
* /content/page/a/jcr:content to be resolved. The appended path is relative, thus appending a path of the form "../../xyz" is supported as well.
*
* @author Olaf Otto
*/
@Documented
@Target({FIELD, ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Reference {
/**
* Append this path segment to the reference path prior to resource resolution.
*/
String append() default "";
}