/* * #%L * wcm.io * %% * Copyright (C) 2014 - 2015 wcm.io * %% * 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. * #L% */ package io.wcm.wcm.ui.granite.resource; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceMetadata; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.SyntheticResource; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.api.wrappers.ValueMapDecorator; import com.day.cq.commons.jcr.JcrConstants; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import io.wcm.sling.commons.resource.ImmutableValueMap; /** * Extended version of {@link SyntheticResource} that allows to pass an own value map and optional child resources. * Please note: Accessing child resources does only work when accessing {@link Resource#listChildren()}, and * not when calling the same method on resourceResolver. This breaks the contract of the resource API, but should * work at least for the Granite UI implementation which seems to always use this method. */ public final class GraniteUiSyntheticResource extends SyntheticResource { private final ValueMap props; private final List<Resource> children; private GraniteUiSyntheticResource(ResourceResolver resourceResolver, ResourceMetadata resourceMetadata, String resourceType, ValueMap props, Iterable<Resource> children) { super(resourceResolver, resourceMetadata, resourceType); this.props = props; this.children = Lists.newArrayList(children); } private GraniteUiSyntheticResource(ResourceResolver resourceResolver, String path, String resourceType, ValueMap props, Iterable<Resource> children) { super(resourceResolver, path, resourceType); this.props = props; this.children = Lists.newArrayList(children); } @SuppressWarnings("unchecked") @Override public <Type> Type adaptTo(Class<Type> type) { if (ValueMap.class.equals(type)) { return (Type)props; } else { return super.adaptTo(type); } } @Override public Iterator<Resource> listChildren() { return children.iterator(); } @Override public Iterable<Resource> getChildren() { return children; } @Override public boolean hasChildren() { return children.iterator().hasNext(); } @Override public Resource getChild(String relPath) { for (Resource resource : children) { // naive implementation that only covers the simplest-possible case to detect the correct child if (StringUtils.equals(resource.getName(), relPath)) { return resource; } } return super.getChild(relPath); } private void addChild(Resource child) { children.add(child); } /** * Create synthetic resource. * @param resourceResolver Resource resolver * @param valueMap Properties * @return Resource */ public static Resource create(ResourceResolver resourceResolver, ValueMap valueMap) { return create(resourceResolver, null, JcrConstants.NT_UNSTRUCTURED, valueMap); } /** * Create synthetic resource. * @param resourceResolver Resource resolver * @param path Resource path * @param resourceType Resource type * @return Resource */ public static Resource create(ResourceResolver resourceResolver, String path, String resourceType) { return create(resourceResolver, path, resourceType, ImmutableValueMap.of()); } /** * Create synthetic resource. * @param resourceResolver Resource resolver * @param path Resource path * @param resourceType Resource type * @param valueMap Properties * @return Resource */ public static Resource create(ResourceResolver resourceResolver, String path, String resourceType, ValueMap valueMap) { return new GraniteUiSyntheticResource(resourceResolver, path, resourceType, valueMap, ImmutableList.<Resource>of()); } /** * Wrap a real resource and create a synthetic resource out of it. * @param resource Real resource * @return Resource */ public static Resource wrap(Resource resource) { return wrap(resource, resource.getValueMap(), resource.getChildren()); } /** * Wrap a real resource and create a synthetic resource out of it. * @param resource Real resource * @param valueMap Properties to use instead of the real properties * @return Resource */ public static Resource wrap(Resource resource, ValueMap valueMap) { return wrap(resource, valueMap, resource.getChildren()); } /** * Wrap a real resource and create a synthetic resource out of it. * Merges the given properties with the existing properties of the resource. * @param resource Real resource * @param valueMap Properties to be merged with the real properties * @return Resource */ public static Resource wrapMerge(Resource resource, ValueMap valueMap) { Map<String, Object> mergedProperties = new HashMap<>(); mergedProperties.putAll(resource.getValueMap()); mergedProperties.putAll(valueMap); return wrap(resource, new ValueMapDecorator(mergedProperties), resource.getChildren()); } private static Resource wrap(Resource resource, ValueMap valueMap, Iterable<Resource> children) { return new GraniteUiSyntheticResource(resource.getResourceResolver(), resource.getResourceMetadata(), resource.getResourceType(), valueMap, children); } /** * Create synthetic resource child resource of the given parent resource. * @param parentResource Parent resource (has to be a {@link GraniteUiSyntheticResource} instance) * @param name Child resource name * @param resourceType Resource type * @return Resource */ public static Resource child(Resource parentResource, String name, String resourceType) { return child(parentResource, name, resourceType, ImmutableValueMap.of()); } /** * Create synthetic resource child resource of the given parent resource. * @param parentResource Parent resource (has to be a {@link GraniteUiSyntheticResource} instance) * @param name Child resource name * @param resourceType Resource type * @param valueMap Properties * @return Resource */ public static Resource child(Resource parentResource, String name, String resourceType, ValueMap valueMap) { Resource child = new GraniteUiSyntheticResource(parentResource.getResourceResolver(), parentResource.getPath() + "/" + name, resourceType, valueMap, ImmutableList.<Resource>of()); if (parentResource instanceof GraniteUiSyntheticResource) { ((GraniteUiSyntheticResource)parentResource).addChild(child); } else { throw new IllegalArgumentException("Resource is not a GraniteUiSyntheticResource."); } return child; } }