View Javadoc

1   /*
2    * Copyright 2000-2005 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.portals.graffito.jcr.persistence.collectionconverter.impl;
18  
19  
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import javax.jcr.Node;
27  import javax.jcr.NodeIterator;
28  import javax.jcr.RepositoryException;
29  import javax.jcr.Session;
30  
31  import org.apache.portals.graffito.jcr.exception.JcrMappingException;
32  import org.apache.portals.graffito.jcr.mapper.Mapper;
33  import org.apache.portals.graffito.jcr.mapper.model.ClassDescriptor;
34  import org.apache.portals.graffito.jcr.mapper.model.CollectionDescriptor;
35  import org.apache.portals.graffito.jcr.persistence.collectionconverter.ManageableCollection;
36  import org.apache.portals.graffito.jcr.persistence.collectionconverter.ManageableCollectionUtil;
37  import org.apache.portals.graffito.jcr.persistence.objectconverter.ObjectConverter;
38  import org.apache.portals.graffito.jcr.reflection.ReflectionUtils;
39  
40  /***
41   * Default Collection Mapping/convertion implementation.
42   *
43   * This collection mapping strategy maps a collection under an extra JCR node (specify by the jcrName in the CollectionDescriptor).
44   * It is usefull when the node type "nt:unstructured" is applied to the collection elements. By this way, it is possible
45   * to distinguish the collection elements from the other main object fields.
46   *
47   * If the collection element class contains an id (see the FieldDescriptor definition), this id value is used to build the collection element node.
48   * Otherwise, the element node name is a simple indexed constant.
49   *
50   * Example - without an id attribute:
51   *   /test (Main object containing the collection field )
52   *     /mycollection (extra node used to store the entire collection)
53   *          /collection-element (node used to store the first collection element)
54   *                /item-prop
55   *                ....
56   *          /collection-element (node used to store the second collection element)
57   *          ...
58   *
59   * Example - with an id attribute:
60   *   /test (Main object containing the collection field )
61   *     /mycollection (extra node used to store the entire collection)
62   *          /aValue (id value assigned to the first element)
63   *                /item-prop
64   *                ....
65   *          /anotherValue (id value assigned to the first element)
66   *          ...
67  
68   * @author <a href="mailto:christophe.lombart@gmail.com">Christophe Lombart</a>
69   * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
70   */
71  public class DefaultCollectionConverterImpl extends AbstractCollectionConverterImpl {
72  
73      private static final String COLLECTION_ELEMENT_NAME = "collection-element";
74  
75      /***
76       * Constructor
77       * @param atomicTypeConverters
78       * @param objectConverter
79       * @param mapper
80       */
81      public DefaultCollectionConverterImpl(Map atomicTypeConverters,
82                                            ObjectConverter objectConverter,
83                                            Mapper mapper) {
84          super(atomicTypeConverters, objectConverter, mapper);
85      }
86  
87      /***
88       * @see AbstractCollectionConverterImpl#doInsertCollection(Session, Node, CollectionDescriptor, ManageableCollection)
89       */
90      protected void doInsertCollection(Session session,
91                                        Node parentNode,
92                                        CollectionDescriptor collectionDescriptor,
93                                        ManageableCollection collection) throws RepositoryException {
94          if (collection == null) {
95              return;
96          }
97  
98          String jcrName = collectionDescriptor.getJcrName();
99  
100         if (jcrName == null) {
101             throw new JcrMappingException(
102                     "The JcrName attribute is not defined for the CollectionDescriptor : "
103                     + collectionDescriptor.getFieldName() + " for the classdescriptor : " + collectionDescriptor.getClassDescriptor().getClassName());
104         }
105 
106         Node collectionNode = parentNode.addNode(jcrName);
107         
108         ClassDescriptor elementClassDescriptor = mapper.getClassDescriptorByClass( ReflectionUtils.forName(collectionDescriptor.getElementClassName())); 
109 
110         Iterator collectionIterator = collection.getIterator();        
111         while (collectionIterator.hasNext()) {
112             Object item = collectionIterator.next();
113             String elementJcrName = null;
114 
115             // If the element object has a unique id => the element jcr node name = the id value
116             if (elementClassDescriptor.hasIdField()) {
117                 String idFieldName = elementClassDescriptor.getIdFieldDescriptor()
118                                                            .getFieldName();
119                 elementJcrName = ReflectionUtils.getNestedProperty(item, idFieldName).toString();
120             }
121             else {                
122                 elementJcrName = COLLECTION_ELEMENT_NAME;
123             }
124 
125             objectConverter.insert(session, collectionNode, elementJcrName, item);
126         }
127     }
128 
129     /***
130      *
131      * @see AbstractCollectionConverterImpl#doUpdateCollection(Session, Node, CollectionDescriptor, ManageableCollection)
132      */
133     protected void doUpdateCollection(Session session,
134                                  Node parentNode,
135                                  CollectionDescriptor collectionDescriptor,
136                                  ManageableCollection collection) throws RepositoryException {
137         
138     	    String jcrName = getCollectionJcrName(collectionDescriptor);
139         if (collection == null)
140         {
141             if (parentNode.hasNode(jcrName)) 
142             {
143                 parentNode.getNode(jcrName).remove();
144             }
145             return;
146         }
147 
148         
149         ClassDescriptor elementClassDescriptor = mapper.getClassDescriptorByClass( ReflectionUtils.forName(collectionDescriptor.getElementClassName()));         
150         Node collectionNode = parentNode.getNode(jcrName);
151         //  If the collection elements have not an id, it is not possible to find the matching JCR nodes => delete the complete collection
152         if (!elementClassDescriptor.hasIdField()) {
153             collectionNode.remove();
154             collectionNode = parentNode.addNode(jcrName);
155         }
156 
157         Iterator collectionIterator = collection.getIterator();
158 
159         Map updatedItems = new HashMap();
160         while (collectionIterator.hasNext()) {
161             Object item = collectionIterator.next();
162 
163             String elementJcrName = null;
164 
165             if (elementClassDescriptor.hasIdField()) {
166 
167                 String idFieldName = elementClassDescriptor.getIdFieldDescriptor().getFieldName();
168                 elementJcrName = ReflectionUtils.getNestedProperty(item, idFieldName).toString();
169 
170                 // Update existing JCR Nodes
171                 if (collectionNode.hasNode(elementJcrName)) {
172                     objectConverter.update(session, collectionNode, elementJcrName, item);
173                 }
174                 else {
175                     // Add new collection elements
176                     objectConverter.insert(session, collectionNode, elementJcrName, item);
177                 }
178 
179                 updatedItems.put(elementJcrName, item);
180             }
181             else {
182                 elementJcrName = COLLECTION_ELEMENT_NAME ;
183                 objectConverter.insert(session, collectionNode, elementJcrName, item);
184             }
185         }
186 
187         // Delete JCR nodes that are not present in the collection
188         if (elementClassDescriptor.hasIdField()) {
189             NodeIterator nodeIterator = collectionNode.getNodes();
190             List removeNodes = new ArrayList();
191             while (nodeIterator.hasNext()) {
192                 Node child = nodeIterator.nextNode();
193                 if (!updatedItems.containsKey(child.getName())) {
194                     removeNodes.add(child);
195                 }
196             }
197             for(int i = 0; i < removeNodes.size(); i++) {
198                 ((Node) removeNodes.get(i)).remove();
199             }
200         }
201     }
202 
203     /***
204      * @see AbstractCollectionConverterImpl#doGetCollection(Session, Node, CollectionDescriptor, Class)
205      */
206     protected ManageableCollection doGetCollection(Session session,
207                                               Node parentNode,
208                                               CollectionDescriptor collectionDescriptor,
209                                               Class collectionFieldClass) throws RepositoryException {
210         String jcrName = getCollectionJcrName(collectionDescriptor);
211 
212         if (parentNode == null || !parentNode.hasNode(jcrName)) {
213             return null;
214         }
215 
216         ManageableCollection collection = ManageableCollectionUtil.getManageableCollection(collectionFieldClass);
217         Node collectionNode = parentNode.getNode(jcrName);
218         NodeIterator children = collectionNode.getNodes();
219         Class elementClass = ReflectionUtils.forName(collectionDescriptor.getElementClassName());
220         
221         while (children.hasNext()) {
222             Node itemNode = children.nextNode();
223             Object item = objectConverter.getObject(session, itemNode.getPath());
224             collection.addObject(item);
225         }
226 
227         return collection;
228     }
229     
230     /***
231      * @see AbstractCollectionConverterImpl#doIsNull(Session, Node, CollectionDescriptor, Class)
232      */
233     protected boolean doIsNull(Session session,
234                                               Node parentNode,
235                                               CollectionDescriptor collectionDescriptor,
236                                               Class collectionFieldClass) throws RepositoryException {
237         String jcrName = getCollectionJcrName(collectionDescriptor);
238 
239         if (parentNode == null || !parentNode.hasNode(jcrName)) {
240             return true;
241         }
242         return false;
243     }    
244 }