import javax.jcr.Value;
import javax.jcr.ValueFactory;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.argeo.ArgeoException;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
-//import org.springframework.beans.BeanWrapperImpl;
-
-public class BeanNodeMapper {
- private final static Log log = LogFactory.getLog(BeanNodeMapper.class);
+public class BeanNodeMapper implements NodeMapper {
+ // private final static Log log = LogFactory.getLog(BeanNodeMapper.class);
private final static String NODE_VALUE = "value";
private Boolean versioning = false;
private Boolean strictUuidReference = false;
+
+ // TODO define a primaryNodeType Strategy
private String primaryNodeType = null;
private ClassLoader classLoader = getClass().getClassLoader();
- public String storagePath(Object obj) {
- String clss = obj.getClass().getName();
- StringBuffer buf = new StringBuffer("/objects/");
- StringTokenizer st = new StringTokenizer(clss, ".");
- while (st.hasMoreTokens()) {
- buf.append(st.nextToken()).append('/');
- }
- buf.append(obj.toString());
- return buf.toString();
- }
+ private NodeMapperProvider nodeMapperProvider;
- public Node save(Session session, Object obj) {
- return save(session, storagePath(obj), obj);
+ /**
+ * exposed method to retrieve a bean from a node
+ */
+ public Object load(Node node) {
+ try {
+ if (nodeMapperProvider != null) {
+ NodeMapper nodeMapper = nodeMapperProvider.findNodeMapper(node);
+ if (nodeMapper != this) {
+ return nodeMapper.load(node);
+ }
+ }
+ return nodeToBean(node);
+ } catch (RepositoryException e) {
+ throw new ArgeoException("Cannot load object from node " + node, e);
+ }
}
+ /** Update an existing node with an object */
public void update(Node node, Object obj) {
try {
- beanToNode(createBeanWrapper(obj), node);
+ if (nodeMapperProvider != null) {
+
+ NodeMapper nodeMapper = nodeMapperProvider.findNodeMapper(node);
+ if (nodeMapper != this) {
+ nodeMapper.update(node, obj);
+ } else
+ beanToNode(createBeanWrapper(obj), node);
+ } else
+ beanToNode(createBeanWrapper(obj), node);
} catch (RepositoryException e) {
throw new ArgeoException("Cannot update node " + node + " with "
+ obj, e);
}
}
+ /**
+ * if no storage path is given; we use canonical path
+ *
+ * @see this.storagePath()
+ */
+ public Node save(Session session, Object obj) {
+ return save(session, storagePath(obj), obj);
+ }
+
+ /**
+ * Create a new node to store an object. If the parentNode doesn't exist, it
+ * is created
+ *
+ * the primaryNodeType may be initialized before
+ */
public Node save(Session session, String path, Object obj) {
try {
- BeanWrapper beanWrapper = createBeanWrapper(obj);
final Node node;
String parentPath = JcrUtils.parentPath(path);
// find or create parent node
Node parentNode;
if (session.itemExists(path))
parentNode = (Node) session.getItem(parentPath);
- else
+ else {
parentNode = JcrUtils.mkdirs(session, parentPath, null,
versioning);
+ }
// create node
+
if (primaryNodeType != null)
node = parentNode.addNode(JcrUtils.lastPathElement(path),
primaryNodeType);
else
node = parentNode.addNode(JcrUtils.lastPathElement(path));
- beanToNode(beanWrapper, node);
+ // Check specific cases
+ if (nodeMapperProvider != null) {
+ NodeMapper nodeMapper = nodeMapperProvider.findNodeMapper(node);
+ if (nodeMapper != this) {
+ nodeMapper.update(node, obj);
+ return node;
+ }
+ }
+ update(node, obj);
return node;
} catch (ArgeoException e) {
throw e;
}
}
- @SuppressWarnings("unchecked")
- public Object nodeToBean(Node node) throws RepositoryException {
-
- String clssName = node.getProperty(classProperty).getValue()
- .getString();
-
- if (log.isDebugEnabled())
- log.debug("Map node " + node.getPath() + " to bean " + clssName);
-
- BeanWrapper beanWrapper = createBeanWrapper(loadClass(clssName));
-
- // process properties
- PropertyIterator propIt = node.getProperties();
- props: while (propIt.hasNext()) {
- Property prop = propIt.nextProperty();
- if (!beanWrapper.isWritableProperty(prop.getName()))
- continue props;
-
- PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(prop
- .getName());
- Class propClass = pd.getPropertyType();
-
- // list
- if (propClass != null && List.class.isAssignableFrom(propClass)) {
- List<Object> lst = new ArrayList<Object>();
- Class<?> valuesClass = classFromProperty(prop);
- if (valuesClass != null)
- for (Value value : prop.getValues()) {
- lst.add(asObject(value, valuesClass));
- }
- continue props;
- }
-
- Object value = asObject(prop.getValue(), pd.getPropertyType());
- if (value != null)
- beanWrapper.setPropertyValue(prop.getName(), value);
+ /**
+ * Parse the FQN of a class to string with '/' delimiters Prefix the
+ * returned string with "/objects/"
+ */
+ public String storagePath(Object obj) {
+ String clss = obj.getClass().getName();
+ StringBuffer buf = new StringBuffer("/objects/");
+ StringTokenizer st = new StringTokenizer(clss, ".");
+ while (st.hasMoreTokens()) {
+ buf.append(st.nextToken()).append('/');
}
+ buf.append(obj.toString());
+ return buf.toString();
+ }
- // process children nodes
- NodeIterator nodeIt = node.getNodes();
- nodes: while (nodeIt.hasNext()) {
- Node childNode = nodeIt.nextNode();
- String name = childNode.getName();
- if (!beanWrapper.isWritableProperty(name))
- continue nodes;
-
- PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(name);
- Class propClass = pd.getPropertyType();
-
- log.debug(childNode.getName() + "=" + propClass);
-
- if (propClass != null && List.class.isAssignableFrom(propClass)) {
- String lstClass = childNode.getProperty(classProperty)
- .getString();
- List<Object> lst;
- try {
- lst = (List<Object>) loadClass(lstClass).newInstance();
- } catch (Exception e) {
- lst = new ArrayList<Object>();
- }
+ @SuppressWarnings("unchecked")
+ /**
+ * Transforms a node into an object of the class defined by classProperty Property
+ */
+ protected Object nodeToBean(Node node) throws RepositoryException {
- NodeIterator valuesIt = childNode.getNodes();
- while (valuesIt.hasNext()) {
- Node lstValueNode = valuesIt.nextNode();
- Object lstValue = nodeToBean(lstValueNode);
- lst.add(lstValue);
+ try {
+ String clssName = node.getProperty(classProperty).getValue()
+ .getString();
+
+ BeanWrapper beanWrapper = createBeanWrapper(loadClass(clssName));
+
+ // process properties
+ PropertyIterator propIt = node.getProperties();
+ props: while (propIt.hasNext()) {
+ Property prop = propIt.nextProperty();
+ if (!beanWrapper.isWritableProperty(prop.getName()))
+ continue props;
+
+ PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(prop
+ .getName());
+ Class propClass = pd.getPropertyType();
+
+ // Process case of List and its derived classes
+ // primitive list
+ if (propClass != null && List.class.isAssignableFrom(propClass)) {
+ List<Object> lst = new ArrayList<Object>();
+ Class<?> valuesClass = classFromProperty(prop);
+ if (valuesClass != null)
+ for (Value value : prop.getValues()) {
+ lst.add(asObject(value, valuesClass));
+ }
+ continue props;
}
- beanWrapper.setPropertyValue(name, lst);
- continue nodes;
+ // Case of other type of property accepted by jcr
+ // Long, Double, String, Binary, Date, Boolean, Name
+ Object value = asObject(prop.getValue(), pd.getPropertyType());
+ if (value != null)
+ beanWrapper.setPropertyValue(prop.getName(), value);
}
- if (propClass != null && Map.class.isAssignableFrom(propClass)) {
- String mapClass = childNode.getProperty(classProperty)
- .getString();
- Map<Object, Object> map;
- try {
- map = (Map<Object, Object>) loadClass(mapClass)
- .newInstance();
- } catch (Exception e) {
- map = new HashMap<Object, Object>();
- }
+ // process children nodes
+ NodeIterator nodeIt = node.getNodes();
+ nodes: while (nodeIt.hasNext()) {
+ Node childNode = nodeIt.nextNode();
+ String name = childNode.getName();
+ if (!beanWrapper.isWritableProperty(name))
+ continue nodes;
+
+ PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(name);
+ Class propClass = pd.getPropertyType();
+
+ // objects list
+ if (propClass != null && List.class.isAssignableFrom(propClass)) {
+ String lstClass = childNode.getProperty(classProperty)
+ .getString();
+ List<Object> lst;
+ try {
+ lst = (List<Object>) loadClass(lstClass).newInstance();
+ } catch (Exception e) {
+ lst = new ArrayList<Object>();
+ }
- // properties
- PropertyIterator keysPropIt = childNode.getProperties();
- keyProps: while (keysPropIt.hasNext()) {
- Property keyProp = keysPropIt.nextProperty();
- // FIXME: use property editor
- String key = keyProp.getName();
- if (classProperty.equals(key))
- continue keyProps;
-
- Class keyPropClass = classFromProperty(keyProp);
- if (keyPropClass != null) {
- Object mapValue = asObject(keyProp.getValue(),
- keyPropClass);
- map.put(key, mapValue);
+ NodeIterator valuesIt = childNode.getNodes();
+ while (valuesIt.hasNext()) {
+ Node lstValueNode = valuesIt.nextNode();
+ Object lstValue = nodeToBean(lstValueNode);
+ lst.add(lstValue);
}
+
+ beanWrapper.setPropertyValue(name, lst);
+ continue nodes;
}
- // node
- NodeIterator keysIt = childNode.getNodes();
- while (keysIt.hasNext()) {
- Node mapValueNode = keysIt.nextNode();
- // FIXME: use property editor
- Object key = mapValueNode.getName();
+ // objects map
+ if (propClass != null && Map.class.isAssignableFrom(propClass)) {
+ String mapClass = childNode.getProperty(classProperty)
+ .getString();
+ Map<Object, Object> map;
+ try {
+ map = (Map<Object, Object>) loadClass(mapClass)
+ .newInstance();
+ } catch (Exception e) {
+ map = new HashMap<Object, Object>();
+ }
+
+ // properties
+ PropertyIterator keysPropIt = childNode.getProperties();
+ keyProps: while (keysPropIt.hasNext()) {
+ Property keyProp = keysPropIt.nextProperty();
+ // FIXME: use property editor
+ String key = keyProp.getName();
+ if (classProperty.equals(key))
+ continue keyProps;
+
+ Class keyPropClass = classFromProperty(keyProp);
+ if (keyPropClass != null) {
+ Object mapValue = asObject(keyProp.getValue(),
+ keyPropClass);
+ map.put(key, mapValue);
+ }
+ }
+
+ // node
+ NodeIterator keysIt = childNode.getNodes();
+ while (keysIt.hasNext()) {
+ Node mapValueNode = keysIt.nextNode();
+ // FIXME: use property editor
+ Object key = mapValueNode.getName();
- Object mapValue = nodeToBean(mapValueNode);
+ Object mapValue = nodeToBean(mapValueNode);
- map.put(key, mapValue);
+ map.put(key, mapValue);
+ }
+ beanWrapper.setPropertyValue(name, map);
+ continue nodes;
}
- beanWrapper.setPropertyValue(name, map);
- continue nodes;
- }
- // default
- Object value = nodeToBean(childNode);
- beanWrapper.setPropertyValue(name, value);
+ // default
+ Object value = nodeToBean(childNode);
+ beanWrapper.setPropertyValue(name, value);
+ }
+ return beanWrapper.getWrappedInstance();
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot map node " + node, e);
}
- return beanWrapper.getWrappedInstance();
}
+ /**
+ * Transforms an object to the specified jcr Node in order to persist it.
+ *
+ * @param beanWrapper
+ * @param node
+ * @throws RepositoryException
+ */
protected void beanToNode(BeanWrapper beanWrapper, Node node)
throws RepositoryException {
- if (log.isDebugEnabled())
- log.debug("Map bean to node " + node.getPath());
-
properties: for (PropertyDescriptor pd : beanWrapper
.getPropertyDescriptors()) {
String name = pd.getName();
-
if (!beanWrapper.isReadableProperty(name))
continue properties;// skip
continue properties;
}
+ // Some bean reference other classes. We must deal with this case
if (value instanceof Class<?>) {
node.setProperty(name, ((Class<?>) value).getName());
continue properties;
}
}
+ /**
+ * Process specific case of list
+ *
+ * @param node
+ * @param name
+ * @param lst
+ * @throws RepositoryException
+ */
protected void addList(Node node, String name, List<?> lst)
throws RepositoryException {
if (node.hasNode(name)) {// update
}
}
+ /**
+ * Process specific case of maps.
+ *
+ * @param node
+ * @param name
+ * @param map
+ * @throws RepositoryException
+ */
protected void addMap(Node node, String name, Map<?, ?> map)
throws RepositoryException {
if (node.hasNode(name)) {// update
}
protected Class<?> loadClass(String name) {
+ // log.debug("Class loader: " + classLoader);
try {
return classLoader.loadClass(name);
} catch (ClassNotFoundException e) {
this.classLoader = classLoader;
}
+ public void setNodeMapperProvider(NodeMapperProvider nodeMapperProvider) {
+ this.nodeMapperProvider = nodeMapperProvider;
+ }
+
+ public String getPrimaryNodeType() {
+ return this.primaryNodeType;
+ }
+
+ public String getClassProperty() {
+ return this.classProperty;
+ }
}