EMMA Coverage Report (generated Sun May 02 20:42:29 CEST 2010)
[all classes][hu.netmind.beankeeper.type.impl]

COVERAGE SUMMARY FOR SOURCE FILE [ContainerHandler.java]

nameclass, %method, %block, %line, %
ContainerHandler.java100% (1/1)100% (7/7)82%  (441/537)91%  (78.9/87)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ContainerHandler100% (1/1)100% (7/7)82%  (441/537)91%  (78.9/87)
unmarshallType (ClassInfo, Object, String, Map, TimeControl): Object 100% (1/1)73%  (40/55)80%  (8/10)
save (ClassInfo, Object, String, Transaction, Long, Object, Set, Set, Set, Li... 100% (1/1)81%  (252/310)91%  (43/47)
hasChanged (ClassInfo, Object, String, Map, Long): boolean 100% (1/1)82%  (107/130)89%  (16.9/19)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
ContainerHandler (): void 100% (1/1)100% (9/9)100% (3/3)
getAttributeTypes (String): Map 100% (1/1)100% (23/23)100% (4/4)
postSave (Object): void 100% (1/1)100% (6/6)100% (3/3)

1/**
2 * Copyright (C) 2007 NetMind Consulting Bt.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19package hu.netmind.beankeeper.type.impl;
20 
21import java.util.*;
22import hu.netmind.beankeeper.common.StoreException;
23import hu.netmind.beankeeper.service.StoreContext;
24import hu.netmind.beankeeper.parser.*;
25import hu.netmind.beankeeper.model.*;
26import hu.netmind.beankeeper.transaction.Transaction;
27import hu.netmind.beankeeper.transaction.TransactionTracker;
28import org.apache.log4j.Logger;
29 
30/**
31 * This is a generic container handler. It manages the ContainerImpl interface.
32 * @author Brautigam Robert
33 * @version Revision: $Revision$
34 */
35public abstract class ContainerHandler extends AbstractTypeHandler
36{
37   private static Logger logger = Logger.getLogger(ContainerHandler.class);
38 
39   private StoreContext context = null; // Injected
40   private TransactionTracker transactionTracker = null; // Injected
41   
42   /**
43    * Get the attribute types that are representing this type
44    * in the original object.
45    */
46   public Map getAttributeTypes(String attributeName)
47   {
48      // Return the attributes in the original object.
49      HashMap attributeTypes = new HashMap();
50      attributeTypes.put(attributeName,Long.class);
51      attributeTypes.put(attributeName+"_itemclass",String.class);
52      return attributeTypes;
53   }
54 
55   /**
56    * Save the collection.
57    */
58   public Object save(ClassInfo classInfo, Object current, String attributeName,
59         Transaction transaction, Long currentSerial,
60         Object newValue, Set waitingObjects, Set saveTables, Set removeTables,
61         List events, Map changedAttributes, Map dbAttributes)
62   {
63      Long lastSerial = (Long) dbAttributes.get(attributeName);
64      // Determine what to do with the null-indicator inside the
65      // parent object. Keep in mind, that something changed!
66      if ( lastSerial == null )
67      {
68         // If current list is null, we assume that
69         // the new list is not null, so insert current serial as last
70         // to indicate the list will be non-null, and to remember version.
71         // If the new list is also null, the next condition will
72         // catch it.
73         changedAttributes.put(attributeName,currentSerial);
74      }
75      if ( newValue == null )
76      {
77         // If list is null, we assume that
78         // previously it was non-null so insert 'null'
79         // to indicate it should be null.
80         changedAttributes.put(attributeName,null);
81         changedAttributes.put(attributeName+"_itemclass",null);
82      }
83      // Save collection
84      String itemClassName = (String) dbAttributes.get(attributeName+"_itemclass");
85      if ( newValue == null )
86      {
87         logger.debug("saving container, new value is null.");
88         // If new value is null, then set the attribute to null
89         // on tracker, and clear all current values.
90         if ( lastSerial != null )
91         {
92            // Clean all values of the current (obsolate list)
93            Map pseudoAttributes = new HashMap();
94            pseudoAttributes.put(attributeName,currentSerial);
95            pseudoAttributes.put(attributeName+"_itemclass",itemClassName);
96            TimeControl currentListTimeControl =
97               new TimeControl(currentSerial,transaction.getSerial(),true);
98            Container currentContainer = (Container) 
99               unmarshallType(classInfo,current,attributeName,pseudoAttributes,currentListTimeControl);
100            currentContainer.clear();
101            currentContainer.save(transaction,currentSerial,waitingObjects,
102                  saveTables, removeTables, events);
103         }
104      } else if ( (newValue instanceof Container) && (((Container)newValue).getParent()==current) && 
105            (((Container)newValue).getLastSerial().equals(lastSerial)) ) {
106         logger.debug("saving container, value is current.");
107         // If the value is the original value, and it is current,
108         // then simply save. Attribute stays the same.
109         ((Container)newValue).save(transaction,currentSerial,waitingObjects,
110            saveTables, removeTables, events);
111         changedAttributes.put(attributeName,currentSerial);
112         changedAttributes.put(attributeName+"_itemclass",((Container)newValue).getItemClassName());
113      } else {
114         if ( logger.isDebugEnabled() )
115            if ( newValue instanceof Container )
116               logger.debug("diffing container, because stayed by parent: "+
117                     (((Container)newValue).getParent()==current)+", serials "+lastSerial+" vs. "+((Container)newValue).getLastSerial());
118            else
119               logger.debug("diffing container, because new value class: "+newValue.getClass().getName());
120         // We must load the current list. The current list is an empty list,
121         // even if the real current list would be null.
122         Map pseudoAttributes = new HashMap();
123         pseudoAttributes.put(attributeName,currentSerial);
124         pseudoAttributes.put(attributeName+"_itemclass",itemClassName);
125         TimeControl currentListTimeControl =
126            new TimeControl(currentSerial,transaction.getSerial(),true);
127         Container currentList = (Container) unmarshallType(classInfo,current,attributeName,pseudoAttributes,currentListTimeControl);
128         if ( logger.isDebugEnabled() )
129            logger.debug("saving container, selected current list: "+currentList.size()+", tracked value time control: "+currentListTimeControl);
130         // Diff to the current list, and save the changes.
131         logger.debug("diffing containers (clean)...");
132         if ( lastSerial != null )
133            currentList.clear(); // Delete entries if the container was not null
134         logger.debug("diffing containers (add)...");
135         currentList.addAll(newValue); // Add new entries
136         // Save
137         logger.debug("diffing containers (save)...");
138         currentList.save(transaction,currentSerial,waitingObjects,
139               saveTables, removeTables, events);
140         // If all went well, update attribute. It is important, that
141         // this list will not be referenced prior to the completion of
142         // save operation, because inserted item may not yet exist at
143         // this point.
144         changedAttributes.put(attributeName,currentSerial);
145         changedAttributes.put(attributeName+"_itemclass",((Container)currentList).getItemClassName());
146         logger.debug("diffing containers finished, final container item class is: "+((Container)currentList).getItemClassName());
147         newValue = currentList;
148         // Inject list into object, so next modification can be optimized
149         classInfo.setAttributeValue(current,attributeName,currentList);
150      }
151      return newValue;
152   }
153 
154   public abstract Class getContainerClass();
155   
156   /**
157    * Return a new instance of Container.
158    */
159   public Object unmarshallType(ClassInfo classInfo, Object obj,
160         String attributeName, Map marshalledValues, TimeControl timeControl)
161   {
162      // First, if the attribute is false, then return null! 
163      Long lastSerial = (Long) marshalledValues.get(attributeName);
164      String itemClassName = (String) marshalledValues.get(attributeName+"_itemclass");
165      if ( lastSerial == null )
166         return null;
167      // Return with implementation
168      try
169      {
170         Container impl = (Container) getContainerClass().newInstance();
171         context.injectServices(impl);
172         impl.init(classInfo, obj, attributeName, itemClassName, lastSerial, timeControl);
173         return impl;
174      } catch ( Throwable e ) {
175         throw new StoreException("could not instantiate collection implementation: "+getContainerClass(),e);
176      }
177   }
178   
179   /**
180    * Determine whether the two values differ.
181    */
182   public boolean hasChanged(ClassInfo info, Object obj, String attributeName, Map dbAttributes, Long currentSerial)
183   {
184      logger.debug("determining whether container attribute: "+attributeName+" changed.");
185      // First get old and new values, and serial
186      Object newValue = info.getAttributeValue(obj,attributeName);
187      Long lastSerial = (Long) dbAttributes.get(attributeName);
188      // If the new value is a Container, we can use a lot of extra
189      // information, so treat it differently.
190      if ( newValue instanceof Container )
191      {
192         // Not changed if:
193         // - new value is for the 'parent' (max. another instance, or version)
194         // - value is current (same as in db)
195         // - has not changed internally
196         boolean result = ! ((((Container)newValue).getParent()==obj) && 
197           ( ((Container)newValue).getLastSerial().equals(lastSerial) ) &&
198           ( ! ((Container)newValue).hasChanged() ));
199         logger.debug("new value is also container, changed: "+result);
200         return result;
201      } else {
202         // Handle nulls
203         if ( lastSerial==null )
204         {
205            logger.debug("last serial is null, new value is null: "+(newValue==null));
206            return newValue!=null;
207         }
208         // New value is not a container, so we must compare to the current
209         // container to know for sure.
210         Transaction tx = transactionTracker.getTransaction(TransactionTracker.TX_OPTIONAL);
211         Long txSerial = null;
212         if ( tx != null )
213            txSerial = tx.getSerial();
214         TimeControl currentControl = new TimeControl(currentSerial,txSerial,txSerial==null?false:true);
215         Container currentImpl = (Container) unmarshallType(info,obj,attributeName,dbAttributes,currentControl);
216         boolean result = ! currentImpl.equals(newValue);
217         logger.debug("new value is not a container impl, compared to current container, changed: "+result);
218         return result;
219      }
220   }
221   
222   public void postSave(Object value)
223   {
224      if ( value != null )
225         ((Container) value).reload();
226   }
227}
228 
229 

[all classes][hu.netmind.beankeeper.type.impl]
EMMA 2.0.5312debian (C) Vladimir Roubtsov