incubator-sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cziege...@apache.org
Subject svn commit: r1519608 - in /sling/trunk/bundles/api/src: main/java/org/apache/sling/api/wrappers/ test/java/org/apache/sling/api/wrappers/
Date Tue, 03 Sep 2013 09:12:45 GMT
Author: cziegeler
Date: Tue Sep  3 09:12:45 2013
New Revision: 1519608

URL: http://svn.apache.org/r1519608
Log:
SLING-2779 :  Support for default properties values of a resource. Apply modified patch from
Gilles Knobloch - I'Ve simplified the values(), keySet(), entrySet() methods and moved the
CompositeValueMap into the wrappers package. I also dropped the util class completely

Added:
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/CompositeValueMap.java
  (with props)
    sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/
    sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/CompositeValueMapTest.java
  (with props)
Modified:
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/package-info.java

Added: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/CompositeValueMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/CompositeValueMap.java?rev=1519608&view=auto
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/CompositeValueMap.java
(added)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/CompositeValueMap.java
Tue Sep  3 09:12:45 2013
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.sling.api.wrappers;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sling.api.resource.ValueMap;
+
+/**
+ * An implementation of the {@link ValueMap} based on two {@link ValueMap}s:
+ * - One containing the properties
+ * - Another one containing the defaults to use in case the properties map
+ *   does not contain the values.
+ * In case you would like to avoid duplicating properties on multiple resources,
+ * you can use a <code>CompositeValueMap</code> to get a concatenated map of
+ * properties.
+ * @since 2.3
+ */
+public class CompositeValueMap implements ValueMap {
+
+    /**
+     * Current properties
+     */
+    private final ValueMap properties;
+
+    /**
+     * Default properties
+     */
+    private final ValueMap defaults;
+
+    /**
+     * Merge mode
+     */
+    private final boolean merge;
+
+    /**
+     * Constructor
+     * @param properties The {@link ValueMap} to read from
+     * @param defaults The default {@link ValueMap} to use as fallback
+     */
+    public CompositeValueMap(final ValueMap properties, final ValueMap defaults) {
+        this(properties, defaults, true);
+    }
+
+    /**
+     * Constructor
+     * @param properties The {@link ValueMap} to read from
+     * @param defaults The default {@link ValueMap} to use as fallback
+     * @param merge Merge flag
+     *              - If <code>true</code>, getting a key would return the
+     *              current property map's value (if available), even if the
+     *              corresponding default does not exist.
+     *              - If <code>false</code>, getting a key would return
+     *              <code>null</code> if the corresponding default does not
+     *              exist
+     */
+    public CompositeValueMap(final ValueMap properties, final ValueMap defaults, boolean
merge) {
+        if (properties == null) {
+            throw new IllegalArgumentException("Properties need to be provided");
+        }
+        this.properties = properties;
+        this.defaults = defaults != null ? defaults : ValueMap.EMPTY;
+        this.merge = merge;
+    }
+
+    // ---- ValueMap
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T get(final String key, final Class<T> type) {
+        if (merge || defaults.containsKey(key)) {
+            // Check if property has been provided, if not use defaults
+            if (properties.containsKey(key)) {
+                return properties.get(key, type);
+            } else {
+                return defaults.get(key, type);
+            }
+        }
+
+        // Override mode and no default value provided for this key
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T get(final String key, final T defaultValue) {
+        if (defaultValue == null) {
+            return (T) get(key);
+        }
+
+        T value = get(key, (Class<T>) defaultValue.getClass());
+        if (value != null) {
+            return value;
+        }
+
+        return defaultValue;
+    }
+
+
+    // ---- Map
+
+    /**
+     * {@inheritDoc}
+     */
+    public int size() {
+        return keySet().size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isEmpty() {
+        if ( defaults.size() > 0 || (merge && properties.size() > 0) ) {
+            return false;
+        }
+        return size() == 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean containsKey(final Object key) {
+        return keySet().contains(key.toString());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean containsValue(final Object value) {
+        return values().contains(value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object get(final Object key) {
+        if (merge || defaults.containsKey(key)) {
+            // Check if property has been provided, if not use defaults
+            if (properties.containsKey(key)) {
+                return properties.get(key);
+            } else {
+                return defaults.get(key);
+            }
+        }
+
+        // Override mode and no default value provided for this key
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object put(final String aKey, final Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object remove(final Object key) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void putAll(final Map<? extends String, ?> properties) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<String> keySet() {
+        return buildAggregatedMap().keySet();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<Object> values() {
+        return buildAggregatedMap().values();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<Entry<String, Object>> entrySet() {
+        return buildAggregatedMap().entrySet();
+    }
+
+    /**
+     * Build the aggregated map containing all values.
+     */
+    private Map<String, Object> buildAggregatedMap() {
+        final Map<String, Object> entries = new HashMap<String, Object>();
+
+        // Add properties in merge mode or if defaults exists
+        for (final Entry<String, Object> entry : properties.entrySet()) {
+            if (merge || defaults.containsKey(entry.getKey())) {
+                entries.put(entry.getKey(), entry.getValue());
+            }
+        }
+
+        // Add missing defaults
+        for (final Entry<String, Object> entry : defaults.entrySet()) {
+            if ( ! entries.containsKey(entry.getKey()) ) {
+                entries.put(entry.getKey(), entry.getValue());
+            }
+        }
+
+        return entries;
+    }
+}

Propchange: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/CompositeValueMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/CompositeValueMap.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/package-info.java?rev=1519608&r1=1519607&r2=1519608&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/package-info.java
(original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/package-info.java
Tue Sep  3 09:12:45 2013
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@Version("2.2")
+@Version("2.3")
 package org.apache.sling.api.wrappers;
 
 import aQute.bnd.annotation.Version;

Added: sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/CompositeValueMapTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/CompositeValueMapTest.java?rev=1519608&view=auto
==============================================================================
--- sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/CompositeValueMapTest.java
(added)
+++ sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/CompositeValueMapTest.java
Tue Sep  3 09:12:45 2013
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.sling.api.wrappers;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.sling.api.resource.ValueMap;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CompositeValueMapTest {
+
+    // Test property names
+    private static final String PROP_NAME_UNCHANGED = "unchangedProp";
+    private static final String PROP_NAME_OVERRIDDEN = "overriddenProp";
+    private static final String PROP_NAME_NEW_TYPE = "newTypeProp";
+    private static final String PROP_NAME_ADDED = "addedProp";
+    private static final String PROP_NAME_DOES_NOT_EXIST = "doesNotExistProp";
+
+    // Default resource's property values
+    private static final String PROP_DEFAULT_UNCHANGED = "Default value of property '" +
PROP_NAME_UNCHANGED + "'";
+    private static final String PROP_DEFAULT_OVERRIDDEN = "Default value of property '" +
PROP_NAME_OVERRIDDEN + "'";
+    private static final String PROP_DEFAULT_NEW_TYPE = "10";
+
+    // Extended resource's property values
+    private static final String PROP_EXTENDED_OVERRIDDEN = "Extended value of property '"
+ PROP_NAME_OVERRIDDEN + "'";
+    private static final Long PROP_EXTENDED_NEW_TYPE = 10L;
+    private static final String PROP_EXTENDED_ADDED = "Extended value of property '" + PROP_NAME_ADDED
+ "'";
+
+    private Map<String, Object> defaultProps = getDefaultProps();
+    private Map<String, Object> extendedProps = getExtendedProps();
+
+    @Test
+    public void testMerge() throws Exception {
+        // Get value map for extended node using default node as defaults
+        CompositeValueMap valueMap = new CompositeValueMap(
+                getExtendedProps(),
+                getDefaultProps()
+        );
+
+        Set<CompositeValueMapTestResult> expectations = new HashSet<CompositeValueMapTestResult>();
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_UNCHANGED));
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_OVERRIDDEN));
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_NEW_TYPE, false, PROP_EXTENDED_NEW_TYPE.getClass()));
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_ADDED));
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_DOES_NOT_EXIST));
+
+        verifyResults(valueMap, expectations);
+    }
+
+    @Test
+    public void testMergeNoDefaults() throws Exception {
+        // Get value map for extended node using an empty default
+        CompositeValueMap valueMap = new CompositeValueMap(
+                getExtendedProps(),
+                null
+        );
+
+        Set<CompositeValueMapTestResult> expectations = new HashSet<CompositeValueMapTestResult>();
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_UNCHANGED, true)); //
Property won't exist as there is no default
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_OVERRIDDEN));
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_NEW_TYPE, false, PROP_EXTENDED_NEW_TYPE.getClass()));
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_ADDED));
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_DOES_NOT_EXIST));
+
+        verifyResults(valueMap, expectations);
+    }
+
+    @Test
+    public void testOverride() throws Exception {
+        // Get value map for extended node using default node as defaults
+        // and override only mode
+        CompositeValueMap valueMap = new CompositeValueMap(
+                getExtendedProps(),
+                getDefaultProps(),
+                false
+        );
+
+        Set<CompositeValueMapTestResult> expectations = new HashSet<CompositeValueMapTestResult>();
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_UNCHANGED));
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_OVERRIDDEN));
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_NEW_TYPE, false, PROP_EXTENDED_NEW_TYPE.getClass()));
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_ADDED, true)); // Property
won't exist as there is no default and it's an override
+        expectations.add(new CompositeValueMapTestResult(PROP_NAME_DOES_NOT_EXIST));
+
+        verifyResults(valueMap, expectations);
+    }
+
+    @Test
+    public void testOverrideNoDefaults() throws Exception {
+        // Get value map for extended node using an empty default
+        // and override only mode
+        CompositeValueMap valueMap = new CompositeValueMap(
+                getExtendedProps(),
+                null,
+                false
+        );
+
+        Assert.assertTrue("Final map should be empty", valueMap.isEmpty());
+    }
+
+    private ValueMap getDefaultProps() {
+        final Map<String, Object> defaultProps = new HashMap<String, Object>();
+
+        defaultProps.put(PROP_NAME_UNCHANGED, PROP_DEFAULT_UNCHANGED);
+        defaultProps.put(PROP_NAME_OVERRIDDEN, PROP_DEFAULT_OVERRIDDEN);
+        defaultProps.put(PROP_NAME_NEW_TYPE, PROP_DEFAULT_NEW_TYPE);
+
+        return new ValueMapDecorator(defaultProps);
+    }
+
+    private ValueMap getExtendedProps() {
+        final Map<String, Object> defaultProps = new HashMap<String, Object>();
+
+        defaultProps.put(PROP_NAME_OVERRIDDEN, PROP_EXTENDED_OVERRIDDEN);
+        defaultProps.put(PROP_NAME_NEW_TYPE, PROP_EXTENDED_NEW_TYPE);
+        defaultProps.put(PROP_NAME_ADDED, PROP_EXTENDED_ADDED);
+
+        return new ValueMapDecorator(defaultProps);
+    }
+
+    private void verifyResults(CompositeValueMap valueMap, Set<CompositeValueMapTestResult>
expectations) {
+        Map<String, Object> expectedMap = new HashMap<String, Object>();
+
+        int expectedSize = 0;
+        for (CompositeValueMapTestResult testResult : expectations) {
+            String property = testResult.propertyName;
+
+            if (testResult.doesNotExist()) {
+                Assert.assertFalse("Property '" + property + "' should NOT exist", valueMap.containsKey(property));
+
+            } else if (testResult.shouldBeDeleted()) {
+                Assert.assertFalse("Property '" + property + "' should NOT be part of the
final map", valueMap.containsKey(property));
+                Assert.assertNull("Property '" + property + "' should be null", valueMap.get(property));
+
+            } else {
+                Assert.assertTrue("Property '" + property + "' should be part of the final
map", valueMap.containsKey(property));
+                expectedSize++;
+
+                if (testResult.shouldBeUnchanged()) {
+                    Assert.assertEquals("Property '" + property + "' should NOT have changed",
testResult.defaultValue, valueMap.get(property));
+                    expectedMap.put(property, testResult.defaultValue);
+                }
+
+                if (testResult.shouldBeOverriden()) {
+                    Assert.assertEquals("Property '" + property + "' should have changed",
testResult.extendedValue, valueMap.get(property));
+                    expectedMap.put(property, testResult.extendedValue);
+                }
+
+                if (testResult.shouldHaveNewType()) {
+                    Assert.assertTrue("Type of property '" + property + "' should have changed",
valueMap.get(property).getClass().equals(testResult.expectedNewType));
+                    expectedMap.put(property, testResult.extendedValue);
+                }
+
+                if (testResult.shouldBeAdded()) {
+                    Assert.assertEquals("Property '" + property + "' should have been added",
testResult.extendedValue, valueMap.get(property));
+                    expectedMap.put(property, testResult.extendedValue);
+                }
+            }
+        }
+
+        Assert.assertEquals("Final map size does NOT match", expectedSize, valueMap.size());
+        Assert.assertEquals("Final map entries do NOT match", expectedMap.entrySet(), valueMap.entrySet());
+        Assert.assertEquals("Final map keys do NOT match", expectedMap.keySet(), valueMap.keySet());
+        Assert.assertTrue("Final map values do NOT match expected: <" + expectedMap.values()
+ "> but was: <" + valueMap.values() + ">", CollectionUtils.isEqualCollection(expectedMap.values(),
valueMap.values()));
+    }
+
+    /**
+     * <code>CompositeValueMapTestResult</code> is an internal helper to analyze
+     * test result and check if the value retrieved from the map matches the
+     * expected value.
+     */
+    private class CompositeValueMapTestResult {
+        private final String propertyName;
+        private final Object defaultValue;
+        private final Object extendedValue;
+        private final boolean shouldBeDeleted;
+        private final Class expectedNewType;
+
+        private CompositeValueMapTestResult(String propertyName) {
+            this(propertyName, false);
+        }
+
+        private CompositeValueMapTestResult(String propertyName, boolean shouldBeDeleted)
{
+            this(propertyName, shouldBeDeleted, null);
+        }
+
+        private CompositeValueMapTestResult(String propertyName, boolean shouldBeDeleted,
Class expectedNewType) {
+            this.propertyName = propertyName;
+            this.defaultValue = defaultProps.get(propertyName);
+            this.extendedValue = extendedProps.get(propertyName);
+            this.shouldBeDeleted = shouldBeDeleted;
+            this.expectedNewType = expectedNewType;
+        }
+
+        /**
+         * Checks if the value should not have changed
+         * @return <code>true</code> if the value should not have changed
+         */
+        boolean shouldBeUnchanged() {
+            return defaultValue != null && extendedValue == null;
+        }
+
+        /**
+         * Checks if the value should have been overridden
+         * @return <code>true</code> if the value should have been overridden
+         */
+        boolean shouldBeOverriden() {
+            return defaultValue != null && extendedValue != null;
+        }
+
+        /**
+         * Checks if the value should have a new type
+         * @return <code>true</code> if the value should have a new type
+         */
+        boolean shouldHaveNewType() {
+            return expectedNewType != null;
+        }
+
+        /**
+         * Checks if the property should have been added
+         * @return <code>true</code> if the property should have been added
+         */
+        boolean shouldBeAdded() {
+            return defaultValue == null && extendedValue != null;
+        }
+
+        /**
+         * Checks if the property should have been deleted
+         * @return <code>true</code> if the property should have been deleted
+         */
+        boolean shouldBeDeleted() {
+            return shouldBeDeleted;
+        }
+
+        /**
+         * Checks if the property should not exist
+         * @return <code>true</code> if the property should not exist
+         */
+        boolean doesNotExist() {
+            return defaultValue == null && extendedValue == null;
+        }
+
+    }
+
+}

Propchange: sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/CompositeValueMapTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/CompositeValueMapTest.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url



Mime
View raw message