You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2009/09/07 17:20:35 UTC
svn commit: r812188 - in /sling/trunk/bundles/jcr/resource/src:
main/java/org/apache/sling/jcr/resource/
main/java/org/apache/sling/jcr/resource/internal/
test/java/org/apache/sling/jcr/resource/
test/java/org/apache/sling/jcr/resource/internal/
Author: cziegeler
Date: Mon Sep 7 15:20:33 2009
New Revision: 812188
URL: http://svn.apache.org/viewvc?rev=812188&view=rev
Log:
SLING-1103 - JCR Value Map Implementations should only be usable for convenience not for extensibility
Added:
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMap.java (with props)
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrPropertyMap.java (with props)
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java (contents, props changed)
- copied, changed from r805442, sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrPropertyMapTest.java (contents, props changed)
- copied, changed from r805442, sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrPropertyMapTest.java
Removed:
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrPropertyMapTest.java
Modified:
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java
Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java?rev=812188&r1=812187&r2=812188&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java Mon Sep 7 15:20:33 2009
@@ -18,148 +18,20 @@
*/
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 org.apache.jackrabbit.util.ISO9075;
-import org.apache.sling.api.resource.PersistableValueMap;
-import org.apache.sling.api.resource.PersistenceException;
/**
* This implementation of the value map allows to change
* the properies and save them later on.
*/
-public class JcrModifiablePropertyMap
- extends JcrPropertyMap
- implements PersistableValueMap {
-
- /** Set of removed and changed properties. */
- private Set<String> changedProperties;
-
- public JcrModifiablePropertyMap(Node node) {
- super(node);
- }
-
- // ---------- Map
-
- /**
- * @see java.util.Map#get(java.lang.Object)
- */
- public Object get(Object key) {
- CacheEntry entry = cache.get(key);
- if (entry == null && !this.fullyRead ) {
- entry = read((String) key);
- }
-
- return entry == null ? null : entry.defaultValue;
- }
+public final class JcrModifiablePropertyMap
+ extends org.apache.sling.jcr.resource.internal.JcrModifiablePropertyMap {
/**
- * @see java.util.Map#clear()
+ * Constructor
+ * @param node The underlying node.
*/
- 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)
- */
- public Object put(String key, Object value) {
- readFully();
- final Object oldValue = this.get(key);
- try {
- this.cache.put(key, new CacheEntry(value, getNode()));
- } catch (RepositoryException re) {
- throw new IllegalArgumentException("Value 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)
- */
- 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 Entry<? extends String, ? extends Object> entry = (Entry<? extends String, ? extends Object>) i.next();
- put(entry.getKey(), entry.getValue());
- }
- }
- }
-
- /**
- * @see java.util.Map#remove(java.lang.Object)
- */
- public Object remove(Object key) {
- 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.toString());
- return oldValue;
- }
-
- /**
- * @see org.apache.sling.api.resource.PersistableValueMap#reset()
- */
- 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()
- */
- public void save() throws PersistenceException {
- if ( this.changedProperties == null || this.changedProperties.size() == 0 ) {
- // nothing has changed
- return;
- }
- try {
- final Node node = getNode();
- for(final String key : this.changedProperties) {
- final String name = ISO9075.encode(key);
- if ( cache.containsKey(key) ) {
- final CacheEntry entry = cache.get(key);
- if ( entry.isMulti ) {
- node.setProperty(name, entry.values);
- } else {
- node.setProperty(name, entry.values[0]);
- }
- } else {
- node.setProperty(name, (String)null);
- }
- }
- node.save();
- } catch (RepositoryException re) {
- throw new PersistenceException("Unable to persist changes.", re);
- }
- this.reset();
+ public JcrModifiablePropertyMap(Node node) {
+ super(node);
}
}
Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java?rev=812188&r1=812187&r2=812188&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java Mon Sep 7 15:20:33 2009
@@ -18,422 +18,20 @@
*/
package org.apache.sling.jcr.resource;
-import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-import javax.jcr.ValueFormatException;
-
-import org.apache.jackrabbit.util.ISO9075;
-import org.apache.sling.api.resource.ValueMap;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* An implementation of the value map based on a JCR node.
* @see JcrModifiablePropertyMap
*/
-public class JcrPropertyMap implements ValueMap {
-
- /** default logger */
- private static Logger LOGGER = LoggerFactory.getLogger(JcrPropertyMap.class);
-
- /** The underlying node. */
- private final Node node;
-
- /** A cache for the properties. */
- final Map<String, CacheEntry> cache;
-
- /** A cache for the values. */
- final Map<String, Object> valueCache;
-
- /** Has the node been read completly? */
- boolean fullyRead;
+public final class JcrPropertyMap
+ extends org.apache.sling.jcr.resource.internal.JcrPropertyMap {
/**
* Constructor
* @param node The underlying node.
*/
public JcrPropertyMap(final Node node) {
- this.node = node;
- this.cache = new LinkedHashMap<String, CacheEntry>();
- this.valueCache = new LinkedHashMap<String, Object>();
- this.fullyRead = false;
- }
-
- /**
- * Get the node.
- */
- Node getNode() {
- return node;
- }
-
- // ---------- ValueMap
-
- /**
- * @see org.apache.sling.api.resource.ValueMap#get(java.lang.String, java.lang.Class)
- */
- @SuppressWarnings("unchecked")
- public <T> T get(final String key, final Class<T> type) {
- if (type == null) {
- return (T) get(key);
- }
-
- CacheEntry entry = cache.get(key);
- if (entry == null) {
- entry = read(key);
- }
- if ( entry == null ) {
- return null;
- }
- return convertToType(entry, type);
- }
-
- /**
- * @see org.apache.sling.api.resource.ValueMap#get(java.lang.String, java.lang.Object)
- */
- @SuppressWarnings("unchecked")
- public <T> T get(final String key,final T defaultValue) {
- if (defaultValue == null) {
- return (T) get(key);
- }
-
- // special handling in case the default value implements one
- // of the interface types supported by the convertToType method
- Class<T> type = (Class<T>) normalizeClass(defaultValue.getClass());
-
- T value = get(key, type);
- if (value == null) {
- value = defaultValue;
- }
-
- return value;
- }
-
- // ---------- Map
-
- /**
- * @see java.util.Map#get(java.lang.Object)
- */
- public Object get(final Object key) {
- if ( key == null ) {
- return null;
- }
- CacheEntry entry = cache.get(key);
- if (entry == null) {
- entry = read((String)key);
- }
- final Object value = (entry == null ? null : entry.defaultValue);
- return value;
- }
-
- /**
- * @see java.util.Map#containsKey(java.lang.Object)
- */
- public boolean containsKey(Object key) {
- return get(key) != null;
- }
-
- /**
- * @see java.util.Map#containsValue(java.lang.Object)
- */
- public boolean containsValue(Object value) {
- readFully();
- return valueCache.containsValue(value);
- }
-
- /**
- * @see java.util.Map#isEmpty()
- */
- public boolean isEmpty() {
- return size() == 0;
- }
-
- /**
- * @see java.util.Map#size()
- */
- public int size() {
- readFully();
- return cache.size();
- }
-
- /**
- * @see java.util.Map#entrySet()
- */
- public Set<java.util.Map.Entry<String, Object>> entrySet() {
- readFully();
- return valueCache.entrySet();
- }
-
- /**
- * @see java.util.Map#keySet()
- */
- public Set<String> keySet() {
- readFully();
- return cache.keySet();
- }
-
- /**
- * @see java.util.Map#values()
- */
- public Collection<Object> values() {
- readFully();
- return valueCache.values();
- }
-
- /**
- * Return the path of the current node.
- *
- * @throws IllegalStateException If a repository exception occurs
- */
- public String getPath() {
- try {
- return node.getPath();
- } catch (RepositoryException e) {
- throw new IllegalStateException(e);
- }
- }
-
- // ---------- Helpers to access the node's property ------------------------
-
- CacheEntry read(final String key) {
-
- // if the node has been completely read, we need not check
- // again, as we certainly will not find the key
- if (fullyRead) {
- return null;
- }
-
- final String name = ISO9075.encode(key);
- try {
- if (node.hasProperty(name)) {
- final Property prop = node.getProperty(name);
- final CacheEntry entry = new CacheEntry(prop);
- cache.put(key, entry);
- valueCache.put(key, entry.defaultValue);
- return entry;
- }
- } catch (RepositoryException re) {
- // TODO: log !!
- }
-
- // property not found or some error accessing it
- return null;
- }
-
- void readFully() {
- if (!fullyRead) {
- try {
- PropertyIterator pi = node.getProperties();
- while (pi.hasNext()) {
- Property prop = pi.nextProperty();
- final String name = prop.getName();
- final String key = ISO9075.decode(name);
- if (!cache.containsKey(key)) {
- final CacheEntry entry = new CacheEntry(prop);
- cache.put(key, entry);
- valueCache.put(key, entry.defaultValue);
- }
- }
- fullyRead = true;
- } catch (RepositoryException re) {
- // TODO: log !!
- }
- }
- }
-
- // ---------- Unsupported Modification methods
-
- public void clear() {
- throw new UnsupportedOperationException();
- }
-
- public Object put(String key, Object value) {
- throw new UnsupportedOperationException();
- }
-
- public void putAll(Map<? extends String, ? extends Object> t) {
- throw new UnsupportedOperationException();
- }
-
- public Object remove(Object key) {
- throw new UnsupportedOperationException();
- }
-
- // ---------- Implementation helper
-
- @SuppressWarnings("unchecked")
- private <T> T convertToType(final CacheEntry entry, Class<T> type) {
- T result = null;
-
- try {
- final boolean array = type.isArray();
-
- if (entry.isMulti) {
-
- if (array) {
-
- result = (T) convertToArray(entry,
- type.getComponentType());
-
- } else if (entry.values.length > 0) {
-
- result = convertToType(entry, -1, entry.values[0], type);
-
- }
-
- } else {
-
- if (array) {
-
- result = (T) convertToArray(entry,
- type.getComponentType());
-
- } else {
-
- result = convertToType(entry, -1, entry.values[0], type);
-
- }
- }
-
- } catch (ValueFormatException vfe) {
- LOGGER.info("converToType: Cannot convert value of " + entry.defaultValue
- + " to " + type, vfe);
- } catch (RepositoryException re) {
- LOGGER.info("converToType: Cannot get value of " + entry.defaultValue, re);
- }
-
- // fall back to nothing
- return result;
- }
-
- private <T> T[] convertToArray(final CacheEntry entry, Class<T> type)
- throws ValueFormatException, RepositoryException {
- List<T> values = new ArrayList<T>();
- for (int i = 0; i < entry.values.length; i++) {
- T value = convertToType(entry, i, entry.values[i], type);
- if (value != null) {
- values.add(value);
- }
- }
-
- @SuppressWarnings("unchecked")
- T[] result = (T[]) Array.newInstance(type, values.size());
-
- return values.toArray(result);
- }
-
- @SuppressWarnings("unchecked")
- private <T> T convertToType(final CacheEntry entry, int index, Value jcrValue,
- Class<T> type) throws ValueFormatException, RepositoryException {
-
- if (String.class == type) {
- return (T) jcrValue.getString();
-
- } else if (Byte.class == type) {
- return (T) Byte.valueOf((byte) jcrValue.getLong());
-
- } else if (Short.class == type) {
- return (T) Short.valueOf((short) jcrValue.getLong());
-
- } else if (Integer.class == type) {
- return (T) Integer.valueOf((int) jcrValue.getLong());
-
- } else if (Long.class == type) {
- if (jcrValue.getType() == PropertyType.BINARY) {
- if (index == -1) {
- return (T) Long.valueOf(entry.property.getLength());
- }
- return (T) Long.valueOf(entry.property.getLengths()[index]);
- }
- return (T) Long.valueOf(jcrValue.getLong());
-
- } else if (Float.class == type) {
- return (T) Float.valueOf((float) jcrValue.getDouble());
-
- } else if (Double.class == type) {
- return (T) Double.valueOf(jcrValue.getDouble());
-
- } else if (Boolean.class == type) {
- return (T) Boolean.valueOf(jcrValue.getBoolean());
-
- } else if (Date.class == type) {
- return (T) jcrValue.getDate().getTime();
-
- } else if (Calendar.class == type) {
- return (T) jcrValue.getDate();
-
- } else if (Value.class == type) {
- return (T) jcrValue;
-
- } else if (Property.class == type) {
- return (T) entry.property;
- }
-
- // fallback in case of unsupported type
- return null;
- }
-
- private Class<?> normalizeClass(Class<?> type) {
- if (Calendar.class.isAssignableFrom(type)) {
- type = Calendar.class;
- } else if (Date.class.isAssignableFrom(type)) {
- type = Date.class;
- } else if (Value.class.isAssignableFrom(type)) {
- type = Value.class;
- } else if (Property.class.isAssignableFrom(type)) {
- type = Property.class;
- }
- return type;
- }
-
- static final class CacheEntry {
- public final Property property;
- public final boolean isMulti;
- public final Value[] values;
-
- public final Object defaultValue;
-
- public CacheEntry(final Property prop)
- throws RepositoryException {
- this.property = prop;
- if ( prop.getDefinition().isMultiple() ) {
- isMulti = true;
- values = prop.getValues();
- } else {
- isMulti = false;
- values = new Value[] {prop.getValue()};
- }
- this.defaultValue = JcrResourceUtil.toJavaObject(prop);
- }
-
- public CacheEntry(final Object value, final Node node)
- throws RepositoryException {
- this.property = null;
- this.defaultValue = value;
- if ( value.getClass().isArray() ) {
- this.isMulti = true;
- final Object[] values = (Object[])value;
- this.values = new Value[values.length];
- for(int i=0; i<values.length; i++) {
- this.values[i] = JcrResourceUtil.createValue(values[i], node.getSession());
- }
- } else {
- this.isMulti = false;
- this.values = new Value[] {JcrResourceUtil.createValue(value, node.getSession())};
- }
- }
+ super(node);
}
}
Added: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMap.java?rev=812188&view=auto
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMap.java (added)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMap.java Mon Sep 7 15:20:33 2009
@@ -0,0 +1,165 @@
+/*
+ * 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.internal;
+
+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 org.apache.jackrabbit.util.ISO9075;
+import org.apache.sling.api.resource.PersistableValueMap;
+import org.apache.sling.api.resource.PersistenceException;
+
+/**
+ * This implementation of the value map allows to change
+ * the properies and save them later on.
+ */
+public class JcrModifiablePropertyMap
+ extends JcrPropertyMap
+ implements PersistableValueMap {
+
+ /** Set of removed and changed properties. */
+ private Set<String> changedProperties;
+
+ public JcrModifiablePropertyMap(Node node) {
+ super(node);
+ }
+
+ // ---------- Map
+
+ /**
+ * @see java.util.Map#get(java.lang.Object)
+ */
+ public Object get(Object key) {
+ CacheEntry entry = cache.get(key);
+ if (entry == null && !this.fullyRead ) {
+ entry = read((String) key);
+ }
+
+ return entry == null ? null : entry.defaultValue;
+ }
+
+ /**
+ * @see java.util.Map#clear()
+ */
+ 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)
+ */
+ public Object put(String key, Object value) {
+ readFully();
+ final Object oldValue = this.get(key);
+ try {
+ this.cache.put(key, new CacheEntry(value, getNode()));
+ } catch (RepositoryException re) {
+ throw new IllegalArgumentException("Value 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)
+ */
+ 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 Entry<? extends String, ? extends Object> entry = (Entry<? extends String, ? extends Object>) i.next();
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ /**
+ * @see java.util.Map#remove(java.lang.Object)
+ */
+ public Object remove(Object key) {
+ 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.toString());
+ return oldValue;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.PersistableValueMap#reset()
+ */
+ 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()
+ */
+ public void save() throws PersistenceException {
+ if ( this.changedProperties == null || this.changedProperties.size() == 0 ) {
+ // nothing has changed
+ return;
+ }
+ try {
+ final Node node = getNode();
+ for(final String key : this.changedProperties) {
+ final String name = ISO9075.encode(key);
+ if ( cache.containsKey(key) ) {
+ final CacheEntry entry = cache.get(key);
+ if ( entry.isMulti ) {
+ node.setProperty(name, entry.values);
+ } else {
+ node.setProperty(name, entry.values[0]);
+ }
+ } else {
+ node.setProperty(name, (String)null);
+ }
+ }
+ node.save();
+ } catch (RepositoryException re) {
+ throw new PersistenceException("Unable to persist changes.", re);
+ }
+ this.reset();
+ }
+}
Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMap.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMap.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMap.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrPropertyMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrPropertyMap.java?rev=812188&view=auto
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrPropertyMap.java (added)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrPropertyMap.java Mon Sep 7 15:20:33 2009
@@ -0,0 +1,440 @@
+/*
+ * 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.internal;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+
+import org.apache.jackrabbit.util.ISO9075;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jcr.resource.JcrResourceUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An implementation of the value map based on a JCR node.
+ * @see JcrModifiablePropertyMap
+ */
+public class JcrPropertyMap implements ValueMap {
+
+ /** default logger */
+ private static Logger LOGGER = LoggerFactory.getLogger(JcrPropertyMap.class);
+
+ /** The underlying node. */
+ private final Node node;
+
+ /** A cache for the properties. */
+ final Map<String, CacheEntry> cache;
+
+ /** A cache for the values. */
+ final Map<String, Object> valueCache;
+
+ /** Has the node been read completly? */
+ boolean fullyRead;
+
+ /**
+ * Constructor
+ * @param node The underlying node.
+ */
+ public JcrPropertyMap(final Node node) {
+ this.node = node;
+ this.cache = new LinkedHashMap<String, CacheEntry>();
+ this.valueCache = new LinkedHashMap<String, Object>();
+ this.fullyRead = false;
+ }
+
+ /**
+ * Get the node.
+ */
+ Node getNode() {
+ return node;
+ }
+
+ // ---------- ValueMap
+
+ /**
+ * @see org.apache.sling.api.resource.ValueMap#get(java.lang.String, java.lang.Class)
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(final String key, final Class<T> type) {
+ if (type == null) {
+ return (T) get(key);
+ }
+
+ CacheEntry entry = cache.get(key);
+ if (entry == null) {
+ entry = read(key);
+ }
+ if ( entry == null ) {
+ return null;
+ }
+ return convertToType(entry, type);
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ValueMap#get(java.lang.String, java.lang.Object)
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(final String key,final T defaultValue) {
+ if (defaultValue == null) {
+ return (T) get(key);
+ }
+
+ // special handling in case the default value implements one
+ // of the interface types supported by the convertToType method
+ Class<T> type = (Class<T>) normalizeClass(defaultValue.getClass());
+
+ T value = get(key, type);
+ if (value == null) {
+ value = defaultValue;
+ }
+
+ return value;
+ }
+
+ // ---------- Map
+
+ /**
+ * @see java.util.Map#get(java.lang.Object)
+ */
+ public Object get(final Object key) {
+ if ( key == null ) {
+ return null;
+ }
+ CacheEntry entry = cache.get(key);
+ if (entry == null) {
+ entry = read((String)key);
+ }
+ final Object value = (entry == null ? null : entry.defaultValue);
+ return value;
+ }
+
+ /**
+ * @see java.util.Map#containsKey(java.lang.Object)
+ */
+ public boolean containsKey(Object key) {
+ return get(key) != null;
+ }
+
+ /**
+ * @see java.util.Map#containsValue(java.lang.Object)
+ */
+ public boolean containsValue(Object value) {
+ readFully();
+ return valueCache.containsValue(value);
+ }
+
+ /**
+ * @see java.util.Map#isEmpty()
+ */
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
+ * @see java.util.Map#size()
+ */
+ public int size() {
+ readFully();
+ return cache.size();
+ }
+
+ /**
+ * @see java.util.Map#entrySet()
+ */
+ public Set<java.util.Map.Entry<String, Object>> entrySet() {
+ readFully();
+ return valueCache.entrySet();
+ }
+
+ /**
+ * @see java.util.Map#keySet()
+ */
+ public Set<String> keySet() {
+ readFully();
+ return cache.keySet();
+ }
+
+ /**
+ * @see java.util.Map#values()
+ */
+ public Collection<Object> values() {
+ readFully();
+ return valueCache.values();
+ }
+
+ /**
+ * Return the path of the current node.
+ *
+ * @throws IllegalStateException If a repository exception occurs
+ */
+ public String getPath() {
+ try {
+ return node.getPath();
+ } catch (RepositoryException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ // ---------- Helpers to access the node's property ------------------------
+
+ CacheEntry read(final String key) {
+
+ // if the node has been completely read, we need not check
+ // again, as we certainly will not find the key
+ if (fullyRead) {
+ return null;
+ }
+
+ final String name = ISO9075.encode(key);
+ try {
+ if (node.hasProperty(name)) {
+ final Property prop = node.getProperty(name);
+ final CacheEntry entry = new CacheEntry(prop);
+ cache.put(key, entry);
+ valueCache.put(key, entry.defaultValue);
+ return entry;
+ }
+ } catch (RepositoryException re) {
+ // TODO: log !!
+ }
+
+ // property not found or some error accessing it
+ return null;
+ }
+
+ void readFully() {
+ if (!fullyRead) {
+ try {
+ PropertyIterator pi = node.getProperties();
+ while (pi.hasNext()) {
+ Property prop = pi.nextProperty();
+ final String name = prop.getName();
+ final String key = ISO9075.decode(name);
+ if (!cache.containsKey(key)) {
+ final CacheEntry entry = new CacheEntry(prop);
+ cache.put(key, entry);
+ valueCache.put(key, entry.defaultValue);
+ }
+ }
+ fullyRead = true;
+ } catch (RepositoryException re) {
+ // TODO: log !!
+ }
+ }
+ }
+
+ // ---------- Unsupported Modification methods
+
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object put(String key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void putAll(Map<? extends String, ? extends Object> t) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ // ---------- Implementation helper
+
+ @SuppressWarnings("unchecked")
+ private <T> T convertToType(final CacheEntry entry, Class<T> type) {
+ T result = null;
+
+ try {
+ final boolean array = type.isArray();
+
+ if (entry.isMulti) {
+
+ if (array) {
+
+ result = (T) convertToArray(entry,
+ type.getComponentType());
+
+ } else if (entry.values.length > 0) {
+
+ result = convertToType(entry, -1, entry.values[0], type);
+
+ }
+
+ } else {
+
+ if (array) {
+
+ result = (T) convertToArray(entry,
+ type.getComponentType());
+
+ } else {
+
+ result = convertToType(entry, -1, entry.values[0], type);
+
+ }
+ }
+
+ } catch (ValueFormatException vfe) {
+ LOGGER.info("converToType: Cannot convert value of " + entry.defaultValue
+ + " to " + type, vfe);
+ } catch (RepositoryException re) {
+ LOGGER.info("converToType: Cannot get value of " + entry.defaultValue, re);
+ }
+
+ // fall back to nothing
+ return result;
+ }
+
+ private <T> T[] convertToArray(final CacheEntry entry, Class<T> type)
+ throws ValueFormatException, RepositoryException {
+ List<T> values = new ArrayList<T>();
+ for (int i = 0; i < entry.values.length; i++) {
+ T value = convertToType(entry, i, entry.values[i], type);
+ if (value != null) {
+ values.add(value);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ T[] result = (T[]) Array.newInstance(type, values.size());
+
+ return values.toArray(result);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T convertToType(final CacheEntry entry, int index, Value jcrValue,
+ Class<T> type) throws ValueFormatException, RepositoryException {
+
+ if (String.class == type) {
+ return (T) jcrValue.getString();
+
+ } else if (Byte.class == type) {
+ return (T) Byte.valueOf((byte) jcrValue.getLong());
+
+ } else if (Short.class == type) {
+ return (T) Short.valueOf((short) jcrValue.getLong());
+
+ } else if (Integer.class == type) {
+ return (T) Integer.valueOf((int) jcrValue.getLong());
+
+ } else if (Long.class == type) {
+ if (jcrValue.getType() == PropertyType.BINARY) {
+ if (index == -1) {
+ return (T) Long.valueOf(entry.property.getLength());
+ }
+ return (T) Long.valueOf(entry.property.getLengths()[index]);
+ }
+ return (T) Long.valueOf(jcrValue.getLong());
+
+ } else if (Float.class == type) {
+ return (T) Float.valueOf((float) jcrValue.getDouble());
+
+ } else if (Double.class == type) {
+ return (T) Double.valueOf(jcrValue.getDouble());
+
+ } else if (Boolean.class == type) {
+ return (T) Boolean.valueOf(jcrValue.getBoolean());
+
+ } else if (Date.class == type) {
+ return (T) jcrValue.getDate().getTime();
+
+ } else if (Calendar.class == type) {
+ return (T) jcrValue.getDate();
+
+ } else if (Value.class == type) {
+ return (T) jcrValue;
+
+ } else if (Property.class == type) {
+ return (T) entry.property;
+ }
+
+ // fallback in case of unsupported type
+ return null;
+ }
+
+ private Class<?> normalizeClass(Class<?> type) {
+ if (Calendar.class.isAssignableFrom(type)) {
+ type = Calendar.class;
+ } else if (Date.class.isAssignableFrom(type)) {
+ type = Date.class;
+ } else if (Value.class.isAssignableFrom(type)) {
+ type = Value.class;
+ } else if (Property.class.isAssignableFrom(type)) {
+ type = Property.class;
+ }
+ return type;
+ }
+
+ static final class CacheEntry {
+ public final Property property;
+ public final boolean isMulti;
+ public final Value[] values;
+
+ public final Object defaultValue;
+
+ public CacheEntry(final Property prop)
+ throws RepositoryException {
+ this.property = prop;
+ if ( prop.getDefinition().isMultiple() ) {
+ isMulti = true;
+ values = prop.getValues();
+ } else {
+ isMulti = false;
+ values = new Value[] {prop.getValue()};
+ }
+ this.defaultValue = JcrResourceUtil.toJavaObject(prop);
+ }
+
+ public CacheEntry(final Object value, final Node node)
+ throws RepositoryException {
+ this.property = null;
+ this.defaultValue = value;
+ if ( value.getClass().isArray() ) {
+ this.isMulti = true;
+ final Object[] values = (Object[])value;
+ this.values = new Value[values.length];
+ for(int i=0; i<values.length; i++) {
+ this.values[i] = JcrResourceUtil.createValue(values[i], node.getSession());
+ }
+ } else {
+ this.isMulti = false;
+ this.values = new Value[] {JcrResourceUtil.createValue(value, node.getSession())};
+ }
+ }
+ }
+}
Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrPropertyMap.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrPropertyMap.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrPropertyMap.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Copied: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java (from r805442, sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java)
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java?p2=sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java&p1=sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java&r1=805442&r2=812188&rev=812188&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java Mon Sep 7 15:20:33 2009
@@ -1,4 +1,4 @@
-package org.apache.sling.jcr.resource;
+package org.apache.sling.jcr.resource.internal;
import java.io.IOException;
import java.util.HashMap;
@@ -8,6 +8,7 @@
import org.apache.sling.api.resource.PersistableValueMap;
import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jcr.resource.JcrResourceUtil;
public class JcrModifiablePropertyMapTest extends JcrPropertyMapTest {
Propchange: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Copied: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrPropertyMapTest.java (from r805442, sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrPropertyMapTest.java)
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrPropertyMapTest.java?p2=sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrPropertyMapTest.java&p1=sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrPropertyMapTest.java&r1=805442&r2=812188&rev=812188&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrPropertyMapTest.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrPropertyMapTest.java Mon Sep 7 15:20:33 2009
@@ -1,4 +1,4 @@
-package org.apache.sling.jcr.resource;
+package org.apache.sling.jcr.resource.internal;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
Propchange: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrPropertyMapTest.java
------------------------------------------------------------------------------
svn:eol-style = native