Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2014 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.adobe.granite.rest.converter.siren;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.util.ISO8601;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.granite.haf.apimodel.impl.ApiModelWrapper;
import com.adobe.granite.haf.apimodel.internal.FilterableChildren;
import com.adobe.granite.haf.api.OrderByDetails;
import com.adobe.granite.rest.Constants;
import com.adobe.granite.rest.converter.ResourceConverter;
import com.adobe.granite.rest.converter.ResourceConverterContext;
import com.adobe.granite.rest.converter.ResourceConverterException;
import com.adobe.granite.rest.filter.Filter;
import com.adobe.granite.rest.utils.Resources;
import com.adobe.granite.rest.utils.URIUtils;
import com.adobe.reef.siren.Action;
import com.adobe.reef.siren.Entity;
import com.adobe.reef.siren.Link;
import com.adobe.reef.siren.builder.BuilderException;
import com.adobe.reef.siren.builder.EntityBuilder;
import com.adobe.reef.siren.builder.LinkBuilder;
/**
* {@code AbtractConverter} is a base implementation of {@link ResourceConverter}. ResourceConverter implementations
* are encouraged to extend from this abstract base class.
*/
public abstract class AbstractSirenConverter implements ResourceConverter {
/**
* Allowed property prefixes for short version
*/
// TODO: make this list configurable?
protected static final String[] PREFIX_ALLOWED_SHORT = {"dc"};
/**
* Allowed property prefixes for long version
*/
// TODO: make this list configurable?
protected static final String[] PREFIX_ALLOWED = {"dc", "cq", "crs", "xmp"};
/**
* SIREN properties prefix
*/
public static final String PREFIX_SRN = "srn:";
protected Logger log = LoggerFactory.getLogger(getClass());
protected Resource resource;
private Collection children;
/**
* Relation attribute name "self".
*/
public static final String REL_SELF = "self";
/**
* Relation attribute name "child".
*/
public static final String REL_CHILD = "child";
/**
* Relation attribute name "content".
*/
public static final String REL_CONTENT = "content";
/**
* Relation attribute name "next".
*/
public static final String REL_NEXT = "next";
/**
* Relation attribute name "prev".
*/
public static final String REL_PREV = "prev";
/**
* Relation attribute name "parent".
*/
public static final String REL_PARENT = "parent";
public AbstractSirenConverter(Resource resource) {
this.resource = resource;
}
protected Collection filterChildren(Map filters, int offset, int limit) {
if (children == null) {
children = new LinkedList();
if (filters != null && !filters.isEmpty() && resource instanceof FilterableChildren) {
for (Iterator it =
((FilterableChildren) resource).filterChildren(filters, Integer.toString(offset), limit,
Collections.emptyList()); it.hasNext();) {
Resource resource = it.next();
children.add(resource);
}
}
}
return children;
}
/**
* Returns the child resources.
* @return A collection of child resources
*/
protected Collection listChildren() {
if (children == null) {
children = new LinkedList();
for (Iterator it = resource.listChildren(); it.hasNext();) {
Resource resource = it.next();
children.add(resource);
}
}
return children;
}
/**
* Checks a {@code key} against a set of {@code allowedPrefixes}.
* @param key Key to check
* @param allowedPrefixes Array of allowed prefixes
* @return {@code true} if key is allowed, {@code false} otherwise
*/
protected boolean isAllowedPrefix(String key, String[] allowedPrefixes) {
return isAllowedPrefix(key, null, allowedPrefixes);
}
/**
* Checks a {@code key} against a set of {@code allowedPrefixes}.
* @param key Key to check
* @param context Converter context
* @param allowedPrefixes Array of allowed prefixes
* @return {@code true} if key is allowed, {@code false} otherwise
*/
protected boolean isAllowedPrefix(String key, ResourceConverterContext context, String[] allowedPrefixes) {
// Ignore props that start with an underscore as those are used internally
if (key.charAt(0) == '_') {
return false;
}
if (context != null && context.getShowProperties() != null && context.getShowProperties().length > 0) {
boolean allowed = ArrayUtils.contains(context.getShowProperties(), key);
if (allowed) {
return allowed;
}
}
String prefix = "";
int pos = key.indexOf(':');
// keys with no prefix are always allowed.
if (pos < 0) {
return true;
}
prefix = key.substring(0, pos);
for (String p : allowedPrefixes) {
if (p.equals(prefix)) {
return true;
}
}
return false;
}
/**
* Returns an array representation of the "class" property.
* @return Siren class property values
*/
protected abstract String[] getClazz();
/**
* Returns an object representation of properties. By default all properties of the underlying resource matching
* the {@link #PREFIX_ALLOWED_SHORT} or if {@code showAllProperties} is set to {@code true} the
* {@link #PREFIX_ALLOWED} are returned.
* @param context Converter context
* @return Siren properties map
*/
protected Map getProperties(ResourceConverterContext context) {
return getProperties(context, false);
}
/**
* Returns an object representation of properties. By default all properties of the underlying resource matching
* the {@link #PREFIX_ALLOWED_SHORT} or if {@code showAllProperties} is set to {@code true} the
* {@link #PREFIX_ALLOWED} are returned.
* @param context Converter context
* @param isChild Indicator for child entity properties
* @return Siren properties map
*/
protected Map getProperties(ResourceConverterContext context, boolean isChild) {
if (!(resource instanceof ApiModelWrapper)) {
ValueMap valueMap = resource.adaptTo(ValueMap.class);
Map props = getProperties(valueMap, context, isChild);
// overwrite name
props.put("name", resource.getName());
return props;
} else {
return new HashMap<>();
}
}
/**
* Returns a set of properties after applying the property prefix rules to the specified {@code properties}.
* @param properties Resource properties
* @param context Converter context
* @param isChild Indicator if resource is a child resource
* @return
*/
private Map getProperties(Map properties, ResourceConverterContext context,
boolean isChild) {
Map props = new HashMap();
if (properties != null) {
for (String key : properties.keySet()) {
// show all allowed
boolean isAllowed = isAllowedPrefix(key, context, PREFIX_ALLOWED);
// show short allowed unless showAllProperties is set
if (isChild) {
if (!context.isShowAllProperties()) {
isAllowed = isAllowedPrefix(key, context, PREFIX_ALLOWED_SHORT);
}
}
if (!isAllowed) {
continue;
}
Object value = properties.get(key);
if (value instanceof Calendar) {
value = ISO8601.format((Calendar) value);
}
if (value instanceof ValueMap) {
value = getProperties((ValueMap) value, context, isChild);
}
props.put(key, value);
}
}
return props;
}
/**
* Returns a list of entities. By default returns an empty list.
* @param context ResourceConverterContext
* @param children Children resources to get entities from
* @return List of Siren sub-entities
* @throws BuilderException If an error occurs during the build of the Entity
*/
protected List getEntities(ResourceConverterContext context, Iterator children)
throws BuilderException {
return new LinkedList();
}
/**
* Returns a list of entities. By default returns an empty list.
* @param context ResourceConverterContext
* @return List of Siren sub-entities
* @throws BuilderException If an error occurs during the build of the Entity
*/
protected List getEntities(ResourceConverterContext context) throws BuilderException {
if (!context.getModelFilters().isEmpty()) {
return getEntities(context,
filterChildren(context.getModelFilters(), context.getOffset(), context.getLimit()).iterator());
}
return getEntities(context, listChildren().iterator());
}
/**
* Returns an entity object.
* @param clazz Class attribute.
* @param title Title attribute. Optional.
* @param actions Actions collection. Optional.
* @param entities Entities collection. Optional.
* @param links Links collection. Optional.
* @param properties Properties collection. Optional.
* @return Siren main entity
* @throws BuilderException If an error occurs during the build of the Entity
*/
protected Entity getEntity(String[] clazz, String title, List actions, List entities,
List links, Map properties) throws BuilderException {
Entity entity =
new EntityBuilder().setClass(clazz).setTitle(title).setActions(actions).setEntities(entities)
.setLinks(links).setProperties(properties).build();
return entity;
}
/**
* Returns a list of links. By default this method returns a list containing one link with rel attribute 'self'
* and the href pointing to itself.
* @param context Converter context
* @return List of Siren links
* @throws BuilderException is an error occurs during building the link
* @throws ResourceConverterException if general error occurs
*/
protected List getLinks(ResourceConverterContext context) throws BuilderException,
ResourceConverterException {
return getLinks(context, false);
}
protected List getLinks(ResourceConverterContext context, boolean isChild) throws BuilderException,
ResourceConverterException {
Map pagingParameters = new HashMap();
if (context.getParameters() != null) {
pagingParameters.putAll(context.getParameters());
}
List links = new LinkedList();
links.add(getLink(AbstractSirenConverter.REL_SELF,
buildURL(context, resource.getPath(), Constants.EXT_JSON, pagingParameters), null));
return links;
}
/**
* Returns a link representation.
* @param rel rel attribute
* @param href href attribute
* @param type type attribute
* @return A Siren link
* @throws BuilderException If an error occurs during the build of the Link
*/
protected Link getLink(String[] rel, String href, String type) throws BuilderException {
Link link = new LinkBuilder().setRel(rel).setHref(href).setType(type).build();
return link;
}
/**
* Returns a link representation.
* @param rel rel attributes
* @param href href attribute
* @param type type attribute
* @return A Siren link
* @throws BuilderException If an error occurs during the build of the Link
*/
protected Link getLink(String rel, String href, String type) throws BuilderException {
return getLink(new String[]{rel}, href, type);
}
/**
* Returns a list of actions. By default this method returns an empty list.
* @param context Converter context
* @return A list of Siren actions
* @throws BuilderException If an error occurs during the build of the Actions
*/
protected List getActions(ResourceConverterContext context) throws BuilderException,
ResourceConverterException {
return new LinkedList();
}
/**
* {@inheritDoc}
*/
@Override
public Entity toEntity(ResourceConverterContext context) throws ResourceConverterException {
try {
ResourceConverterContext ctx = (ResourceConverterContext) context;
Entity entity =
new EntityBuilder().setClass(getClazz()).setProperties(getProperties(ctx))
.setEntities(getEntities(ctx)).setLinks(getLinks(ctx)).setActions(getActions(ctx)).build();
return entity;
} catch (BuilderException e) {
throw new ResourceConverterException(e);
}
}
@Override
public Entity toSubEntity(ResourceConverterContext context) throws ResourceConverterException {
try {
Entity entity =
new EntityBuilder().setClass(getClazz()).setRel(new String[]{AbstractSirenConverter.REL_CHILD})
.setProperties(getProperties(context, true)).setLinks(getLinks(context, true)).build();
return entity;
} catch (BuilderException e) {
throw new ResourceConverterException(e);
}
}
/**
* Builds an URL from the provided {@code resourcePath} and {@code extension} parameters.
* @param context Converter context. Cannot be null.
* @param resourcePath Resource path to build a URL from. Cannot be null.
* @param extension Resource extension with leading dot or {@code null}
* @return A string representation of an URL
* @throws ResourceConverterException if a general error occurs
*/
protected String buildURL(ResourceConverterContext context, String resourcePath, String extension)
throws ResourceConverterException {
return buildURL(context, resourcePath, extension, null);
}
/**
* Builds an URL pointing to the 'next' results for paging.
* @param context Converter context
* @return A string representation of an URL or {@code null} if there is nothing more to show
* @throws ResourceConverterException if an error occurs
*/
protected String getNextPageURL(ResourceConverterContext context) throws ResourceConverterException {
int offset = context.getOffset();
int limit = context.getLimit();
Filter filter = context.getFilter();
if (!context.getModelFilters().isEmpty()) {
if (offset + limit >= Resources.getSize(filterChildren(context.getModelFilters(), offset, limit)
.iterator(), filter)) {
return null;
}
}
if (offset + limit >= Resources.getSize(listChildren().iterator(), filter)) {
return null;
}
offset += limit;
return buildPagingURL(context, offset);
}
/**
* Builds an URL pointing to the 'previous' results for paging.
* @param context Converter context
* @return A string representation of an URL
* @throws ResourceConverterException if a general error occurs
*/
protected String getPrevPageURL(ResourceConverterContext context) throws ResourceConverterException {
int offset = context.getOffset();
if (offset == 0) {
return null;
}
int limit = context.getLimit();
offset -= limit;
if (offset < 0) {
offset = 0;
}
return buildPagingURL(context, offset);
}
private String buildPagingURL(ResourceConverterContext context, int offset) throws ResourceConverterException {
Map pagingParameters = new LinkedHashMap();
pagingParameters.putAll(context.getParameters());
pagingParameters.put(Constants.PARAM_OFFSET, new String[]{Integer.toString(offset)});
pagingParameters.put(Constants.PARAM_LIMIT, new String[]{Integer.toString(context.getLimit())});
return buildURL(context, resource.getPath(), Constants.EXT_JSON, pagingParameters);
}
private String buildURL(ResourceConverterContext context, String resourcePath, String extension,
Map additionalParameters) throws ResourceConverterException {
String authority = context.getServerName();
// we won't expose default ports
if (context.getServerPort() != 80 && context.getServerPort() != 443) {
authority += ":" + context.getServerPort();
}
String path =
(StringUtils.isEmpty(context.getContextPath()) ? "" : StringUtils
.removeEnd(context.getContextPath(), "/")) + resourcePath;
if (!context.isAbsolutURI()) {
path = URIUtils.relativize(context.getRequestPathInfo(), resourcePath);
}
if (extension != null) {
path += extension;
}
String query = null;
if (additionalParameters != null) {
boolean isFirst = true;
for (String parameter : additionalParameters.keySet()) {
for (String value : additionalParameters.get(parameter)) {
if (!isFirst) {
query += "&";
} else {
query = "";
isFirst = false;
}
query += parameter;
query += "=";
query += value;
}
}
}
try {
if (!context.isAbsolutURI()) {
return new URI(null, null, path, query, null).toASCIIString();
} else {
return new URI(context.getScheme(), authority, path, query, null).toASCIIString();
}
} catch (URISyntaxException e) {
throw new ResourceConverterException(e.getMessage(), e);
}
}
}