This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.resourcemerger-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourcemerger.git
commit a2fed2887c5ebe2e7da77a349709e5023e11833c Author: Carsten Ziegeler <cziege...@apache.org> AuthorDate: Wed Jan 29 12:59:42 2014 +0000 SLING-2986 : Merged Resource Provider. Apply contribution by Gilles Knobloch git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/resourcemerger@1562431 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 108 ++++++++++++ .../sling/resourcemerger/impl/MergedResource.java | 180 ++++++++++++++++++++ .../impl/MergedResourceConstants.java | 51 ++++++ .../impl/MergedResourceProvider.java | 185 +++++++++++++++++++++ .../impl/MergedResourceProviderFactory.java | 69 ++++++++ .../sling/resourcemerger/impl/MergedValueMap.java | 166 ++++++++++++++++++ 6 files changed, 759 insertions(+) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6985712 --- /dev/null +++ b/pom.xml @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sling</groupId> + <artifactId>sling</artifactId> + <version>18</version> + <relativePath /> + </parent> + + <artifactId>org.apache.sling.resourcemerger</artifactId> + <packaging>bundle</packaging> + <version>0.0.1-SNAPSHOT</version> + + <name>Apache Sling Resource Merger</name> + <description> + This bundle provides services to merge resources. + </description> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/resourcemerger</connection> + <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/resourcemerger</developerConnection> + <url>http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger</url> + </scm> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.sling</groupId> + <artifactId>maven-sling-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + </dependency> + + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.api</artifactId> + <version>2.5.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.osgi</artifactId> + <version>2.2.0</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.0.1</version> + <scope>provided</scope> + </dependency> + + <!-- Test dependencies --> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.testing</artifactId> + <version>2.0.14</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>javax.jcr</groupId> + <artifactId>jcr</artifactId> + <version>2.0</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/src/main/java/org/apache/sling/resourcemerger/impl/MergedResource.java b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResource.java new file mode 100644 index 0000000..542a70a --- /dev/null +++ b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResource.java @@ -0,0 +1,180 @@ +/* + * 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.resourcemerger.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.sling.api.resource.AbstractResource; +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.ResourceUtil; +import org.apache.sling.api.resource.ValueMap; + +/** + * {@inheritDoc} + */ +public class MergedResource extends AbstractResource { + + private final ResourceResolver resolver; + private final String mergeRootPath; + private final String relativePath; + private final List<String> mappedResources = new ArrayList<String>(); + + /** + * Constructor + * + * @param resolver Resource resolver + * @param mergeRootPath Merge root path + * @param relativePath Relative path + */ + MergedResource(ResourceResolver resolver, String mergeRootPath, String relativePath) { + this.resolver = resolver; + this.mergeRootPath = mergeRootPath; + this.relativePath = relativePath; + } + + /** + * Constructor + * + * @param resolver Resource resolver + * @param mergeRootPath Merge root path + * @param relativePath Relative path + * @param mappedResources List of physical mapped resources' paths + */ + MergedResource(ResourceResolver resolver, String mergeRootPath, String relativePath, List<String> mappedResources) { + this.resolver = resolver; + this.mergeRootPath = mergeRootPath; + this.relativePath = relativePath; + this.mappedResources.addAll(mappedResources); + } + + + // ---- MergedResource interface ------------------------------------------ + + public String getRelativePath() { + return relativePath; + } + + /** + * {@inheritDoc} + */ + public void addMappedResource(String path) { + mappedResources.add(path); + } + + /** + * {@inheritDoc} + */ + public Iterable<String> getMappedResources() { + return mappedResources; + } + + + // ---- Resource interface ------------------------------------------------ + + /** + * {@inheritDoc} + */ + public String getPath() { + return ResourceUtil.normalize(mergeRootPath + "/" + relativePath); + } + + /** + * {@inheritDoc} + */ + public Iterator<Resource> listChildren() { + return resolver.listChildren(this); + } + + /** + * {@inheritDoc} + */ + public String getResourceType() { + return relativePath; + } + + /** + * {@inheritDoc} + */ + public String getResourceSuperType() { + // So far, there's no concept of resource super type for a merged resource + return null; + } + + /** + * {@inheritDoc} + */ + public ResourceMetadata getResourceMetadata() { + ResourceMetadata metadata = new ResourceMetadata(); + metadata.put(ResourceMetadata.RESOLUTION_PATH, getPath()); + metadata.put("sling.mergedResource", true); + metadata.put("sling.mappedResources", mappedResources.toArray(new String[mappedResources.size()])); + return metadata; + } + + /** + * {@inheritDoc} + */ + public ResourceResolver getResourceResolver() { + return resolver; + } + + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) { + if (type == ValueMap.class) { + return (AdapterType) new MergedValueMap(this); + } + + return null; + } + + + // ---- Object ------------------------------------------------------------ + + /** + * Merged resources are considered equal if their paths are equal, + * regardless of the list of mapped resources. + * + * @param o Object to compare with + * @return Returns <code>true</code> if the two merged resources have the + * same path. + */ + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (o == this) { + return true; + } + if (o.getClass() != getClass()) { + return false; + } + + Resource r = (Resource) o; + return r.getPath().equals(getPath()); + } + +} diff --git a/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceConstants.java b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceConstants.java new file mode 100644 index 0000000..9e86ef4 --- /dev/null +++ b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceConstants.java @@ -0,0 +1,51 @@ +/* + * 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.resourcemerger.impl; + +/** + * Constants related to {@link MergedResource}. + */ +public class MergedResourceConstants { + + private MergedResourceConstants() { + // Make sure it never gets instantiated + } + + /** + * Property name containing the list of properties to hide. + */ + public static final String PN_HIDE_PROPERTIES = "sling:hideProperties"; + + /** + * Property name which has to be set to <code>true</code> to hide the + * whole resource (and its children) of the current resource. + */ + public static final String PN_HIDE_RESOURCE = "sling:hideResource"; + + /** + * Property name containing the list of child resources to hide. + */ + public static final String PN_HIDE_CHILDREN = "sling:hideChildren"; + + /** + * Property name for the reordering option. + */ + public static final String PN_ORDER_BEFORE = "sling:orderBefore"; + +} diff --git a/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProvider.java b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProvider.java new file mode 100644 index 0000000..64d835c --- /dev/null +++ b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProvider.java @@ -0,0 +1,185 @@ +/* + * 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.resourcemerger.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceProvider; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceUtil; +import org.apache.sling.api.resource.ValueMap; + +/** + * The <code>MergedResourceProvider</code> is the resource provider providing + * access to {@link MergedResource} objects. + */ +public class MergedResourceProvider implements ResourceProvider { + + private final String mergeRootPath; + + public MergedResourceProvider(String mergeRootPath) { + this.mergeRootPath = mergeRootPath; + } + + /** + * {@inheritDoc} + */ + public Resource getResource(ResourceResolver resolver, HttpServletRequest request, String path) { + return getResource(resolver, path); + } + + /** + * {@inheritDoc} + */ + public Resource getResource(ResourceResolver resolver, String path) { + List<String> mappedResources = new ArrayList<String>(); + + if (resolver.getSearchPath() != null) { + String relativePath = getRelativePath(path); + + // Loop over provided base paths + for (String basePath : resolver.getSearchPath()) { + // Try to get the corresponding physical resource for this base path + Resource baseRes = resolver.getResource(ResourceUtil.normalize(basePath + "/" + relativePath)); + if (baseRes != null) { + // Physical resource exists, add it to the list of mapped resources + mappedResources.add(0, baseRes.getPath()); + } + } + + if (!mappedResources.isEmpty()) { + // Create a new merged resource based on the list of mapped physical resources + return new MergedResource(resolver, mergeRootPath, relativePath, mappedResources); + } + } + + // Either base paths were not defined, or the resource does not exist in any of them + return null; + } + + /** + * {@inheritDoc} + */ + public Iterator<Resource> listChildren(Resource resource) { + if (resource instanceof MergedResource) { + MergedResource mergedResource = (MergedResource) resource; + ResourceResolver resolver = mergedResource.getResourceResolver(); + List<Resource> children = new ArrayList<Resource>(); + + for (String mappedResourcePath : mergedResource.getMappedResources()) { + Resource mappedResource = resolver.getResource(mappedResourcePath); + + // Check if the resource exists + if (mappedResource == null) { + continue; + } + + // Check if some previously defined children have to be ignored + ValueMap mappedResourceProps = mappedResource.adaptTo(ValueMap.class); + List<String> childrenToHide = Arrays.asList(mappedResourceProps.get(MergedResourceConstants.PN_HIDE_CHILDREN, new String[0])); + if (childrenToHide.isEmpty()) { + String childToHide = mappedResourceProps.get(MergedResourceConstants.PN_HIDE_CHILDREN, String.class); + if (childToHide != null) { + childrenToHide.add(childToHide); + } + } + if (childrenToHide.contains("*")) { + // Clear current children list + children.clear(); + } else { + // Go through current children in order to hide them individually + Iterator<Resource> it = children.iterator(); + while (it.hasNext()) { + if (childrenToHide.contains(it.next().getName())) { + it.remove(); + } + } + } + + // Browse children of current physical resource + for (Resource child : mappedResource.getChildren()) { + String childRelativePath = ResourceUtil.normalize(mergedResource.getRelativePath() + "/" + child.getName()); + + if (child.adaptTo(ValueMap.class).get(MergedResourceConstants.PN_HIDE_RESOURCE, Boolean.FALSE)) { + // Child resource has to be hidden + children.remove(new MergedResource(resolver, mergeRootPath, childRelativePath)); + + } else { + // Check if the child resource already exists in the children list + MergedResource mergedResChild = new MergedResource(resolver, mergeRootPath, childRelativePath); + int mergedResChildIndex = -1; + if (children.contains(mergedResChild)) { + // Get current index of the merged resource's child + mergedResChildIndex = children.indexOf(mergedResChild); + mergedResChild = (MergedResource) children.get(mergedResChildIndex); + } + // Add a new mapped resource to the merged resource + mergedResChild.addMappedResource(child.getPath()); + boolean mergedResChildExists = mergedResChildIndex > -1; + + // Check if children need reordering + int orderBeforeIndex = -1; + String orderBefore = ResourceUtil.getValueMap(child).get(MergedResourceConstants.PN_ORDER_BEFORE, String.class); + if (orderBefore != null && !orderBefore.equals(mergedResChild.getName())) { + // Get a dummy merged resource just to know the index of that merged resource + MergedResource orderBeforeRes = new MergedResource(resolver, mergeRootPath, mergedResource.getRelativePath() + "/" + orderBefore); + if (children.contains(orderBeforeRes)) { + orderBeforeIndex = children.indexOf(orderBeforeRes); + } + } + + if (orderBeforeIndex > -1) { + // Add merged resource's child at the right position + children.add(orderBeforeIndex, mergedResChild); + if (mergedResChildExists) { + children.remove(mergedResChildIndex > orderBeforeIndex ? ++mergedResChildIndex : mergedResChildIndex); + } + } else if (!mergedResChildExists) { + // Only add the merged resource's child if it did not exist yet + children.add(mergedResChild); + } + } + } + } + + return children.iterator(); + } + + // Return null for resources that aren't a MergedResource + return null; + } + + /** + * Gets the relative path out of merge root path + * + * @param path Absolute path + * @return Relative path + */ + private String getRelativePath(String path) { + return StringUtils.removeStart(path, mergeRootPath); + } + +} diff --git a/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderFactory.java b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderFactory.java new file mode 100644 index 0000000..0a49607 --- /dev/null +++ b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderFactory.java @@ -0,0 +1,69 @@ +/* + * 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.resourcemerger.impl; + +import java.util.Map; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Properties; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.ResourceProvider; +import org.apache.sling.api.resource.ResourceProviderFactory; +import org.apache.sling.commons.osgi.PropertiesUtil; + +@Component(metatype = false) +@Service(value = ResourceProviderFactory.class) +@Properties({ + @Property(name = ResourceProvider.ROOTS, value = {"/merge"}, propertyPrivate = true) +}) +/** + * The <code>MergedResourceProviderFactory</code> creates merged resource + * providers. + */ +public class MergedResourceProviderFactory implements ResourceProviderFactory { + + private String mergeRootPath; + + /** + * {@inheritDoc} + */ + public ResourceProvider getResourceProvider(Map<String, Object> stringObjectMap) throws LoginException { + return new MergedResourceProvider(mergeRootPath); + } + + /** + * {@inheritDoc} + */ + public ResourceProvider getAdministrativeResourceProvider(Map<String, Object> stringObjectMap) throws LoginException { + return new MergedResourceProvider(mergeRootPath); + } + + @Activate + private void configure(Map<String, ?> properties) { + String[] mergeRootPaths = PropertiesUtil.toStringArray(properties.get(ResourceProvider.ROOTS), new String[0]); + if (mergeRootPaths.length > 0) { + mergeRootPath = mergeRootPaths[0]; + } + } + +} diff --git a/src/main/java/org/apache/sling/resourcemerger/impl/MergedValueMap.java b/src/main/java/org/apache/sling/resourcemerger/impl/MergedValueMap.java new file mode 100644 index 0000000..a9606fe --- /dev/null +++ b/src/main/java/org/apache/sling/resourcemerger/impl/MergedValueMap.java @@ -0,0 +1,166 @@ +/* + * 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.resourcemerger.impl; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.sling.api.resource.ResourceUtil; +import org.apache.sling.api.resource.ValueMap; + +/** + * A <code>MergedValueMap</code> is a {@link ValueMap} aggregated from the + * different resources mapped to a {@link MergedResource}. + */ +public class MergedValueMap implements ValueMap { + + /** + * Set of properties to exclude from override + */ + private static final Set<String> EXCLUDED_PROPERTIES = new HashSet<String>(); + + static { + EXCLUDED_PROPERTIES.add(MergedResourceConstants.PN_HIDE_PROPERTIES); + EXCLUDED_PROPERTIES.add(MergedResourceConstants.PN_HIDE_RESOURCE); + EXCLUDED_PROPERTIES.add(MergedResourceConstants.PN_HIDE_CHILDREN); + EXCLUDED_PROPERTIES.add(MergedResourceConstants.PN_ORDER_BEFORE); + } + + /** + * Final properties + */ + private Map<String, Object> properties = new LinkedHashMap<String, Object>(); + + /** + * Constructor + * + * @param resource The merged resource to get properties from + */ + public MergedValueMap(MergedResource resource) { + // Iterate over physical resources + for (String r : resource.getMappedResources()) { + ValueMap vm = ResourceUtil.getValueMap(resource.getResourceResolver().getResource(r)); + if (properties.isEmpty()) { + // Add all properties + properties.putAll(vm); + } else { + // Get properties to add or override + for (String key : vm.keySet()) { + if (!isExcludedProperty(key)) { + properties.put(key, vm.get(key)); + } + } + + // Get properties to hide + String[] propertiesToHide = vm.get(MergedResourceConstants.PN_HIDE_PROPERTIES, new String[0]); + if (propertiesToHide.length == 0) { + String propertyToHide = vm.get(MergedResourceConstants.PN_HIDE_PROPERTIES, String.class); + if (propertyToHide != null) { + propertiesToHide = new String[]{propertyToHide}; + } + } + for (String propName : propertiesToHide) { + if (propName.equals("*")) { + properties.clear(); + break; + } else { + properties.remove(propName); + } + } + } + + // Hide excluded properties + for (String excludedProperty : EXCLUDED_PROPERTIES) { + properties.remove(excludedProperty); + } + } + } + + @SuppressWarnings("unchecked") + public <T> T get(String name, Class<T> type) { + return (T) properties.get(name); + } + + @SuppressWarnings("unchecked") + public <T> T get(String name, T defaultValue) { + Object o = properties.get(name); + return o == null ? defaultValue : (T) o; + } + + public int size() { + return properties != null ? properties.size() : 0; + } + + public boolean isEmpty() { + return properties == null || properties.isEmpty(); + } + + public boolean containsKey(Object o) { + return properties != null && properties.containsKey(o); + } + + public boolean containsValue(Object o) { + return properties != null && properties.containsValue(o); + } + + public Object get(Object o) { + return properties != null ? properties.get(o) : null; + } + + public Object put(String s, Object o) { + return properties != null ? properties.put(s, o) : null; + } + + public Object remove(Object o) { + return properties != null ? properties.remove(o) : null; + } + + public void putAll(Map<? extends String, ?> map) { + if (properties != null) { + properties.putAll(map); + } + } + + public void clear() { + if (properties != null) { + properties.clear(); + } + } + + public Set<String> keySet() { + return properties != null ? properties.keySet() : Collections.<String>emptySet(); + } + + public Collection<Object> values() { + return properties != null ? properties.values() : Collections.emptyList(); + } + + public Set<Entry<String, Object>> entrySet() { + return properties != null ? properties.entrySet() : Collections.<Entry<String, Object>>emptySet(); + } + + private boolean isExcludedProperty(String key) { + return EXCLUDED_PROPERTIES.contains(key); + } + +} -- To stop receiving notification emails like this one, please contact "commits@sling.apache.org" <commits@sling.apache.org>.