]> git.argeo.org Git - lgpl/argeo-commons.git/blob - server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jcr/BeanNodeMapper.java
931b11f5ca612e0b7688352137adecb1c136ade2
[lgpl/argeo-commons.git] / server / runtime / org.argeo.server.jackrabbit / src / main / java / org / argeo / jcr / BeanNodeMapper.java
1 package org.argeo.jcr;
2
3 import java.beans.PropertyDescriptor;
4 import java.beans.PropertyEditor;
5 import java.io.InputStream;
6 import java.util.Calendar;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.StringTokenizer;
10
11 import javax.jcr.Item;
12 import javax.jcr.ItemNotFoundException;
13 import javax.jcr.Node;
14 import javax.jcr.RepositoryException;
15 import javax.jcr.Session;
16 import javax.jcr.Value;
17 import javax.jcr.ValueFactory;
18
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.argeo.ArgeoException;
22 import org.springframework.beans.BeanWrapper;
23 import org.springframework.beans.BeanWrapperImpl;
24
25 public class BeanNodeMapper {
26 private final static Log log = LogFactory.getLog(BeanNodeMapper.class);
27
28 private Boolean versioning = false;
29 private String uuidProperty = "uuid";
30 private String classProperty = "class";
31 private Boolean strictUuidReference = false;
32 private String primaryNodeType = null;
33
34 public String storagePath(Object obj) {
35 String clss = obj.getClass().getName();
36 StringBuffer buf = new StringBuffer("/objects/");
37 StringTokenizer st = new StringTokenizer(clss, ".");
38 while (st.hasMoreTokens()) {
39 buf.append(st.nextToken()).append('/');
40 }
41 buf.append(obj.toString());
42 return buf.toString();
43 }
44
45 public Node saveOrUpdate(Session session, Object obj) {
46 return saveOrUpdate(session, storagePath(obj), obj);
47 }
48
49 public Node saveOrUpdate(Session session, String path, Object obj) {
50 try {
51 BeanWrapper beanWrapper = new BeanWrapperImpl(obj);
52 final Node node;
53 if (session.itemExists(path)) {
54 Item item = session.getItem(path);
55 if (!item.isNode())
56 throw new ArgeoException("An item exist under " + path
57 + " but it is not a node: " + item);
58 node = (Node) item;
59 } else {
60 String parentPath = JcrUtils.parentPath(path);
61 Node parentNode;
62 if (session.itemExists(path))
63 parentNode = (Node) session.getItem(parentPath);
64 else
65 parentNode = JcrUtils.mkdirs(session, parentPath, null,
66 versioning);
67 // create node
68 if (primaryNodeType != null)
69 node = parentNode.addNode(JcrUtils.lastPathElement(path),
70 primaryNodeType);
71 else
72 node = parentNode.addNode(JcrUtils.lastPathElement(path));
73 }
74
75 beanToNode(session, beanWrapper, node);
76 return node;
77 } catch (ArgeoException e) {
78 throw e;
79 } catch (Exception e) {
80 throw new ArgeoException("Cannot save or update " + obj + " under "
81 + path, e);
82 }
83 }
84
85 protected void beanToNode(Session session, BeanWrapper beanWrapper,
86 Node node) throws RepositoryException {
87 if (log.isDebugEnabled())
88 log.debug("Map bean to node " + node.getPath());
89
90 properties: for (PropertyDescriptor pd : beanWrapper
91 .getPropertyDescriptors()) {
92 String name = pd.getName();
93
94 Object value = beanWrapper.getPropertyValue(name);
95 if (value == null)
96 continue properties;// skip
97
98 // if (uuidProperty != null && uuidProperty.equals(name)) {
99 // // node.addMixin(ArgeoJcrConstants.MIX_REFERENCEABLE);
100 // node.setProperty(ArgeoJcrConstants.JCR_UUID, value.toString());
101 // continue properties;
102 // }
103
104 if ("class".equals(name)) {
105 if (classProperty != null) {
106 node.setProperty(classProperty, ((Class<?>) value)
107 .getName());
108 // TODO: store a class hierarchy?
109 }
110 continue properties;
111 }
112
113 Value val = asValue(session, value);
114 if (val != null) {
115 node.setProperty(name, val);
116 continue properties;
117 }
118
119 if (value instanceof List<?>) {
120 List<?> lst = (List<?>) value;
121 Value[] values = new Value[lst.size()];
122 boolean atLeastOneSet = false;
123 for (int i = 0; i < lst.size(); i++) {
124 Object lstValue = lst.get(i);
125 values[i] = asValue(session, lstValue);
126 if (values[i] != null) {
127 atLeastOneSet = true;
128 } else {
129 Node childNode = findChildReference(session,
130 new BeanWrapperImpl(lstValue));
131 if (childNode != null) {
132 values[i] = session.getValueFactory().createValue(
133 childNode);
134 atLeastOneSet = true;
135 }
136 }
137 }
138
139 if (!atLeastOneSet && lst.size() != 0)
140 throw new ArgeoException(
141 "This type of list is not supported "
142 + lst.getClass());
143
144 node.setProperty(name, values);
145 continue properties;
146 }
147
148 if (value instanceof Map<?, ?>) {
149 Map<?, ?> map = (Map<?, ?>) value;
150 // TODO: add map specific type
151 Node mapNode = node.addNode(name);
152 for (Object key : map.keySet()) {
153 Object mapValue = map.get(key);
154 PropertyEditor pe = beanWrapper.findCustomEditor(key
155 .getClass(), null);
156 String keyStr = pe.getAsText();
157 // TODO: check string format
158 Node entryNode = mapNode.addNode(keyStr);
159 beanToNode(session, new BeanWrapperImpl(mapValue),
160 entryNode);
161 }
162
163 continue properties;
164 }
165
166 BeanWrapper child = new BeanWrapperImpl(value);
167 // TODO: delegate to another mapper
168
169 Node childNode = findChildReference(session, child);
170 if (childNode != null) {
171 node.setProperty(name, childNode);
172 continue properties;
173 }
174
175 // default case (recursive)
176 childNode = node.addNode(name);
177 beanToNode(session, child, childNode);
178 // if (childNode.isNodeType(ArgeoJcrConstants.MIX_REFERENCEABLE)) {
179 // log.debug("Add reference to " + childNode.getPath());
180 // node.setProperty(name, childNode);
181 // }
182 }
183 }
184
185 /** Returns null if value cannot be found */
186 protected Value asValue(Session session, Object value)
187 throws RepositoryException {
188 ValueFactory valueFactory = session.getValueFactory();
189 if (value instanceof Integer)
190 return valueFactory.createValue((Integer) value);
191 else if (value instanceof Long)
192 return valueFactory.createValue((Long) value);
193 else if (value instanceof Float)
194 return valueFactory.createValue((Float) value);
195 else if (value instanceof Double)
196 return valueFactory.createValue((Double) value);
197 else if (value instanceof Boolean)
198 return valueFactory.createValue((Boolean) value);
199 else if (value instanceof Calendar)
200 return valueFactory.createValue((Calendar) value);
201 else if (value instanceof CharSequence)
202 return valueFactory.createValue(value.toString());
203 else if (value instanceof InputStream)
204 return valueFactory.createValue((InputStream) value);
205 else
206 return null;
207 }
208
209 protected Node findChildReference(Session session, BeanWrapper child)
210 throws RepositoryException {
211 if (child.isReadableProperty(uuidProperty)) {
212 String childUuid = child.getPropertyValue(uuidProperty).toString();
213 try {
214 return session.getNodeByUUID(childUuid);
215 } catch (ItemNotFoundException e) {
216 if (strictUuidReference)
217 throw new ArgeoException("No node found with uuid "
218 + childUuid, e);
219 }
220 }
221 return null;
222 }
223
224 protected String propertyName(String name) {
225 return name;
226 }
227
228 public void setVersioning(Boolean versioning) {
229 this.versioning = versioning;
230 }
231
232 }