3 import java
.beans
.PropertyDescriptor
;
4 import java
.beans
.PropertyEditor
;
5 import java
.io
.InputStream
;
6 import java
.util
.Calendar
;
9 import java
.util
.StringTokenizer
;
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
;
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
;
25 public class BeanNodeMapper
{
26 private final static Log log
= LogFactory
.getLog(BeanNodeMapper
.class);
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;
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('/');
41 buf
.append(obj
.toString());
42 return buf
.toString();
45 public Node
saveOrUpdate(Session session
, Object obj
) {
46 return saveOrUpdate(session
, storagePath(obj
), obj
);
49 public Node
saveOrUpdate(Session session
, String path
, Object obj
) {
51 BeanWrapper beanWrapper
= new BeanWrapperImpl(obj
);
53 if (session
.itemExists(path
)) {
54 Item item
= session
.getItem(path
);
56 throw new ArgeoException("An item exist under " + path
57 + " but it is not a node: " + item
);
60 String parentPath
= JcrUtils
.parentPath(path
);
62 if (session
.itemExists(path
))
63 parentNode
= (Node
) session
.getItem(parentPath
);
65 parentNode
= JcrUtils
.mkdirs(session
, parentPath
, null,
68 if (primaryNodeType
!= null)
69 node
= parentNode
.addNode(JcrUtils
.lastPathElement(path
),
72 node
= parentNode
.addNode(JcrUtils
.lastPathElement(path
));
75 beanToNode(session
, beanWrapper
, node
);
77 } catch (ArgeoException e
) {
79 } catch (Exception e
) {
80 throw new ArgeoException("Cannot save or update " + obj
+ " under "
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());
90 properties
: for (PropertyDescriptor pd
: beanWrapper
91 .getPropertyDescriptors()) {
92 String name
= pd
.getName();
94 Object value
= beanWrapper
.getPropertyValue(name
);
96 continue properties
;// skip
98 // if (uuidProperty != null && uuidProperty.equals(name)) {
99 // // node.addMixin(ArgeoJcrConstants.MIX_REFERENCEABLE);
100 // node.setProperty(ArgeoJcrConstants.JCR_UUID, value.toString());
101 // continue properties;
104 if ("class".equals(name
)) {
105 if (classProperty
!= null) {
106 node
.setProperty(classProperty
, ((Class
<?
>) value
)
108 // TODO: store a class hierarchy?
113 Value val
= asValue(session
, value
);
115 node
.setProperty(name
, val
);
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;
129 Node childNode
= findChildReference(session
,
130 new BeanWrapperImpl(lstValue
));
131 if (childNode
!= null) {
132 values
[i
] = session
.getValueFactory().createValue(
134 atLeastOneSet
= true;
139 if (!atLeastOneSet
&& lst
.size() != 0)
140 throw new ArgeoException(
141 "This type of list is not supported "
144 node
.setProperty(name
, values
);
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
156 String keyStr
= pe
.getAsText();
157 // TODO: check string format
158 Node entryNode
= mapNode
.addNode(keyStr
);
159 beanToNode(session
, new BeanWrapperImpl(mapValue
),
166 BeanWrapper child
= new BeanWrapperImpl(value
);
167 // TODO: delegate to another mapper
169 Node childNode
= findChildReference(session
, child
);
170 if (childNode
!= null) {
171 node
.setProperty(name
, childNode
);
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);
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
);
209 protected Node
findChildReference(Session session
, BeanWrapper child
)
210 throws RepositoryException
{
211 if (child
.isReadableProperty(uuidProperty
)) {
212 String childUuid
= child
.getPropertyValue(uuidProperty
).toString();
214 return session
.getNodeByUUID(childUuid
);
215 } catch (ItemNotFoundException e
) {
216 if (strictUuidReference
)
217 throw new ArgeoException("No node found with uuid "
224 protected String
propertyName(String name
) {
228 public void setVersioning(Boolean versioning
) {
229 this.versioning
= versioning
;