All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.granite.rest.utils.DeepModifiableValueMapDecorator Maven / Gradle / Ivy

There is a newer version: 6.5.21
Show newest version
/*************************************************************************
 *
 * 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.utils;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.ArrayUtils;
import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.wrappers.ModifiableValueMapDecorator;
import org.apache.sling.api.wrappers.ValueMapDecorator;

/**
 * A value map wrapper which implements deep reading/writing of properties
 * based on the resource tree and a set of exposed sub-nodes. Note that only
 * properties of exposed sub-nodes can be updated.
 */
public class DeepModifiableValueMapDecorator extends ModifiableValueMapDecorator {

    protected final String pathPrefix;

    protected final ResourceResolver resolver;

    protected final Map base;

    protected final String[] subnodes;

    public DeepModifiableValueMapDecorator(final Resource resource, final Map base, final String[] subnodes) {
        super(base);
        this.pathPrefix = resource.getPath() + "/";
        this.resolver = resource.getResourceResolver();
        this.base = base;
        this.subnodes = subnodes;
    }

    public DeepModifiableValueMapDecorator(final Resource resource, final Map base) {
        this(resource, base, new String[]{});
    }

    protected ValueMap getValueMap(final String name) {
        final int pos = name.lastIndexOf("/");
        if ( pos == -1 ) {
            return new ModifiableValueMapDecorator(this.base);
        }
        final Resource rsrc = this.resolver.getResource(pathPrefix + name.substring(0, pos));
        if ( rsrc != null ) {
            final ValueMap vm = rsrc.adaptTo(ModifiableValueMap.class);
            if ( vm != null ) {
                return new ModifiableValueMapDecorator(vm);
            }
        }
        // fall back
        return ModifiableValueMap.EMPTY;
    }

    protected String getPropertyName(final String name) {
        final int pos = name.lastIndexOf("/");
        if ( pos == -1 ) {
            return name;
        }
        return name.substring(pos + 1);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  T get(String name, final Class type) {
        return this.getValueMap(name).get(this.getPropertyName(name), type);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  T get(String name, T defaultValue) {
        return this.getValueMap(name).get(this.getPropertyName(name), defaultValue);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean containsKey(final Object key) {
        if ( key == null ) {
            return false;
        }
        final String name = key.toString();
        return this.getValueMap(name).containsKey(this.getPropertyName(name));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object get(final Object key) {
        if ( key == null ) {
            return null;
        }
        String name = key.toString();
        if (ArrayUtils.contains(subnodes, name)) {
            return this.getValueMap(name + "/");
        }
        return this.getValueMap(name).get(this.getPropertyName(name));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object put(String key, Object value) {
        if ( key == null ) {
            return null;
        }
        final String name = key.toString();
        // only allow exposed sub-nodes to be updated
        if (name.indexOf("/") > -1) {
            String subnode = name.substring(0, name.indexOf("/"));
            if (ArrayUtils.contains(subnodes, subnode)) {
                return this.getValueMap(name).put(this.getPropertyName(name), value);
            } else {
                return null;
            }
        }
        return this.getValueMap(name).put(this.getPropertyName(name), value);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void putAll(Map t) {
        for (String key : t.keySet()) {
            put(key, t.get(key));
        }
    }

    /**
     * {@inheritDoc}
     */
    public Set keySet() {
        return buildAggregatedMap().keySet();
    }

    /**
     * {@inheritDoc}
     */
    public Collection values() {
        return buildAggregatedMap().values();
    }

    /**
     * {@inheritDoc}
     */
    public Set> entrySet() {
        return buildAggregatedMap().entrySet();
    }

    /**
     * Build the aggregated map containing all values of the base ValueMap plus
     * the sub-nodes as nested ValueMap's.
     */
    private ValueMap buildAggregatedMap() {
        final ValueMap entries = new ValueMapDecorator(new HashMap());
        
        // Add properties from base
        for (final Entry entry : getValueMap("").entrySet()) {
            entries.put(entry.getKey(), entry.getValue());
        }
        
        // Add subnodes 
        for (String subnode : subnodes) {
            // suffix for resolution
            entries.put(subnode, getValueMap(subnode + "/"));
        }
        
        return entries;
    }

}