| /* |
| * 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.jcr.resource; |
| |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.jcr.Node; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.Value; |
| |
| import org.apache.sling.api.resource.PersistableValueMap; |
| import org.apache.sling.api.resource.PersistenceException; |
| import org.apache.sling.jcr.resource.internal.NodeUtil; |
| import org.apache.sling.jcr.resource.internal.helper.JcrPropertyMapCacheEntry; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * This implementation of the value map allows to change |
| * the properties and save them later on. |
| * |
| * @deprecated Resources should be adapted to a modifiable value map instead |
| */ |
| @SuppressWarnings("deprecation") |
| @Deprecated |
| public final class JcrModifiablePropertyMap |
| extends JcrPropertyMap |
| implements PersistableValueMap { |
| |
| private static volatile boolean LOG_DEPRECATED = true; |
| |
| /** Set of removed and changed properties. */ |
| private Set<String> changedProperties; |
| |
| /** |
| * Constructor |
| * @param node The underlying node. |
| */ |
| public JcrModifiablePropertyMap(final Node node) { |
| super(node); |
| if ( LOG_DEPRECATED ) { |
| LOG_DEPRECATED = false; |
| LoggerFactory.getLogger(this.getClass()).warn("DEPRECATION WARNING: JcrModifiablePropertyMap is deprecated. Please switch to resource API."); |
| } |
| } |
| |
| /** |
| * Constructor |
| * @param node The underlying node. |
| * @param dynamicCL Dynamic class loader for loading serialized objects. |
| * @since 2.0.6 |
| */ |
| public JcrModifiablePropertyMap(final Node node, final ClassLoader dynamicCL) { |
| super(node, dynamicCL); |
| if ( LOG_DEPRECATED ) { |
| LOG_DEPRECATED = false; |
| LoggerFactory.getLogger(this.getClass()).warn("DEPRECATION WARNING: JcrModifiablePropertyMap is deprecated. Please switch to resource API."); |
| } |
| } |
| |
| // ---------- Map |
| /** |
| * @see java.util.Map#clear() |
| */ |
| @Override |
| public void clear() { |
| // we have to read all properties first |
| this.readFully(); |
| if ( this.changedProperties == null ) { |
| this.changedProperties = new HashSet<String>(); |
| } |
| this.changedProperties.addAll(this.cache.keySet()); |
| this.cache.clear(); |
| this.valueCache.clear(); |
| } |
| |
| /** |
| * @see java.util.Map#put(java.lang.Object, java.lang.Object) |
| */ |
| @Override |
| public Object put(String aKey, Object value) { |
| final String key = checkKey(aKey); |
| if ( key.indexOf('/') != -1 ) { |
| throw new IllegalArgumentException("Invalid key: " + key); |
| } |
| if ( value == null ) { |
| throw new NullPointerException("Value should not be null (key = " + key + ")"); |
| } |
| readFully(); |
| final Object oldValue = this.get(key); |
| try { |
| this.cache.put(key, new JcrPropertyMapCacheEntry(value, this.getNode())); |
| } catch (final RepositoryException re) { |
| throw new IllegalArgumentException("Value for key " + key + " can't be put into node: " + value, re); |
| } |
| this.valueCache.put(key, value); |
| if ( this.changedProperties == null ) { |
| this.changedProperties = new HashSet<String>(); |
| } |
| this.changedProperties.add(key); |
| return oldValue; |
| } |
| |
| /** |
| * @see java.util.Map#putAll(java.util.Map) |
| */ |
| @Override |
| public void putAll(Map<? extends String, ? extends Object> t) { |
| readFully(); |
| if ( t != null ) { |
| final Iterator<?> i = t.entrySet().iterator(); |
| while (i.hasNext() ) { |
| @SuppressWarnings("unchecked") |
| final Map.Entry<? extends String, ? extends Object> entry = (Map.Entry<? extends String, ? extends Object>) i.next(); |
| put(entry.getKey(), entry.getValue()); |
| } |
| } |
| } |
| |
| /** |
| * @see java.util.Map#remove(java.lang.Object) |
| */ |
| @Override |
| public Object remove(Object aKey) { |
| final String key = checkKey(aKey.toString()); |
| readFully(); |
| final Object oldValue = this.cache.remove(key); |
| this.valueCache.remove(key); |
| if ( this.changedProperties == null ) { |
| this.changedProperties = new HashSet<String>(); |
| } |
| this.changedProperties.add(key); |
| return oldValue; |
| } |
| |
| /** |
| * @see org.apache.sling.api.resource.PersistableValueMap#reset() |
| */ |
| @Override |
| public void reset() { |
| if ( this.changedProperties != null ) { |
| this.changedProperties = null; |
| } |
| this.cache.clear(); |
| this.valueCache.clear(); |
| this.fullyRead = false; |
| } |
| |
| /** |
| * @see org.apache.sling.api.resource.PersistableValueMap#save() |
| */ |
| @Override |
| @SuppressWarnings("javadoc") |
| public void save() throws PersistenceException { |
| if ( this.changedProperties == null || this.changedProperties.size() == 0 ) { |
| // nothing has changed |
| return; |
| } |
| try { |
| final Node node = getNode(); |
| // check for mixin types |
| if ( this.changedProperties.contains(NodeUtil.MIXIN_TYPES) ) { |
| if ( cache.containsKey(NodeUtil.MIXIN_TYPES) ) { |
| final JcrPropertyMapCacheEntry entry = cache.get(NodeUtil.MIXIN_TYPES); |
| NodeUtil.handleMixinTypes(node, entry.convertToType(String[].class, node, getDynamicClassLoader())); |
| } else { |
| // remove all mixin types! |
| NodeUtil.handleMixinTypes(node, null); |
| } |
| } |
| |
| for(final String key : this.changedProperties) { |
| final String name = escapeKeyName(key); |
| if ( !NodeUtil.MIXIN_TYPES.equals(name) ) { |
| if ( cache.containsKey(key) ) { |
| final JcrPropertyMapCacheEntry entry = cache.get(key); |
| if ( entry.isArray() ) { |
| node.setProperty(name, entry.convertToType(Value[].class, node, getDynamicClassLoader())); |
| } else { |
| node.setProperty(name, entry.convertToType(Value.class, node, getDynamicClassLoader())); |
| } |
| } else { |
| if ( node.hasProperty(name) ) { |
| node.getProperty(name).remove(); |
| } |
| } |
| } |
| } |
| node.getSession().save(); |
| this.reset(); |
| } catch (final RepositoryException re) { |
| throw new PersistenceException("Unable to persist changes.", re, getPath(), null); |
| } |
| } |
| } |