package edu.ualberta.med.biobank.common.wrappers; import edu.ualberta.med.biobank.common.exception.BiobankCheckException; import edu.ualberta.med.biobank.common.exception.BiobankException; import edu.ualberta.med.biobank.common.exception.BiobankQueryResultSizeException; import edu.ualberta.med.biobank.common.exception.DuplicateEntryException; import edu.ualberta.med.biobank.common.wrappers.WrapperTransaction.TaskList; import edu.ualberta.med.biobank.common.wrappers.listener.WrapperEvent; import edu.ualberta.med.biobank.common.wrappers.listener.WrapperListener; import edu.ualberta.med.biobank.common.wrappers.loggers.LogAction; import edu.ualberta.med.biobank.common.wrappers.loggers.LogGroup; import edu.ualberta.med.biobank.common.wrappers.loggers.WrapperLogProvider; import edu.ualberta.med.biobank.common.wrappers.util.ProxyUtil; import edu.ualberta.med.biobank.common.wrappers.util.WrapperUtil; import edu.ualberta.med.biobank.model.Log; import edu.ualberta.med.biobank.server.applicationservice.BiobankApplicationService; import edu.ualberta.med.biobank.util.NullHelper; import gov.nih.nci.system.applicationservice.ApplicationException; import gov.nih.nci.system.applicationservice.WritableApplicationService; import gov.nih.nci.system.query.hibernate.HQLCriteria; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.hibernate.Hibernate; import org.hibernate.criterion.CriteriaSpecification; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Restrictions; @SuppressWarnings("unused") public abstract class ModelWrapper<E> implements Comparable<ModelWrapper<E>> { final Map<Property<?, ?>, Object> propertyCache = new HashMap<Property<?, ?>, Object>(); protected WritableApplicationService appService; protected E wrappedObject; protected PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport( this); protected HashMap<String, Object> cache = new HashMap<String, Object>(); private final List<WrapperListener> listeners = new ArrayList<WrapperListener>(); private final ElementTracker<E> elementTracker = new ElementTracker<E>(this); private final ElementQueue<E> elementQueue = new ElementQueue<E>(this); private final WrapperCascader<E> cascader = new WrapperCascader<E>(this); private final WrapperChecker<E> preChecker = new WrapperChecker<E>(this); WrapperSession session; public ModelWrapper(WritableApplicationService appService, E wrappedObject) { this.appService = appService; this.wrappedObject = wrappedObject; this.session = new WrapperSession(this); } public ModelWrapper(WritableApplicationService appService) { this.appService = appService; try { this.wrappedObject = getNewObject(); } catch (Exception e) { Class<E> classType = getWrappedClass(); if (classType != null) { throw new RuntimeException( "was not able to create new object of type " //$NON-NLS-1$ + classType.getName(), e); } throw new RuntimeException("was not able to create new object", //$NON-NLS-1$ e); } this.session = new WrapperSession(this); } public E getWrappedObject() { return wrappedObject; } public abstract Property<Integer, ? super E> getIdProperty(); public void setWrappedObject(E newWrappedObject) { E oldWrappedObject = wrappedObject; wrappedObject = newWrappedObject; clear(); session = new WrapperSession(this); firePropertyChanges(oldWrappedObject, newWrappedObject); } public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { List<Property<?, ? super E>> propertiesList = getProperties(); if ((propertiesList == null) || (propertiesList.size() == 0)) { throw new RuntimeException("wrapper has not defined any properties"); //$NON-NLS-1$ } for (Property<?, ? super E> property : propertiesList) { if (property.getPropertyChangeName().equals(propertyName)) { propertyChangeSupport.addPropertyChangeListener(propertyName, listener); return; } } throw new RuntimeException("invalid property: " + propertyName); //$NON-NLS-1$ } public void removePropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.removePropertyChangeListener(listener); } public boolean isNew() { return (getId() == null); } public Integer getId() { return getIdProperty().get(wrappedObject); } public void setId(Integer id) { getIdProperty().set(wrappedObject, id); } public WritableApplicationService getAppService() { return appService; } @Deprecated public void persist() throws Exception { WrapperTransaction.persist(this, appService); } @Deprecated public void delete() throws Exception { WrapperTransaction.delete(this, appService); } /** * Will be deprecated in version 3.3.0 */ public void reload() throws Exception { clear(); E oldWrappedObject = wrappedObject; if (!isNew()) { wrappedObject = getObjectFromDatabase(); if (wrappedObject == null) { wrappedObject = getNewObject(); } } session = new WrapperSession(this); firePropertyChanges(oldWrappedObject, wrappedObject); } /** * Add tasks to the given {@link TaskList} that will persist (i.e. insert or * update) the wrapped model object. The {@link TaskList}-s might also check * certain conditions on the client or server, as well as persist potential * dependent objects. * <p> * This method should be overridden as necessary to add to the * {@link TaskList} so the wrapped model object is properly persisted. * <p> * <strong>IMPORTANT.</strong> Checks can also be added to the * {@link TaskList}. However, in general, checks should be performed using * HQL <em>after</em> the object is persisted (and related objects are * cascaded) so that the database's state can be verified. This is opposed * to checking the in-memory model objects, since it cannot be easily * determined which will or have actually been persisted. Checks can be done * on the in-memory model objects (before persisting) before the database * throws an error, but it is often difficult to know what values to check. * * @param tasks where to add the tasks */ @Deprecated protected void addPersistTasks(TaskList tasks) { tasks.add(new PersistModelWrapperQueryTask<E>(this)); tasks.add(check().stringLengths()); } /** * Add tasks to the given {@link TaskList} that will delete the wrapped * model object. The {@link TaskList}-s might also check certain conditions * on the client or server, as well as persist potential dependent objects. * <p> * This method should be overridden as necessary to add to the * {@link TaskList} so the wrapped model object is properly deleted. * * @param tasks where to add the tasks */ @Deprecated protected void addDeleteTasks(TaskList tasks) { tasks.add(new DeleteModelWrapperQueryTask<E>(this)); } /** * Same as addPersistTasks() excepts includes logging. * * @param tasks */ @Deprecated protected final void addPersistAndLogTasks(TaskList tasks) { addPersistTasks(tasks); log(LogAction.Type.PERSIST, tasks); } /** * Same as addDeleteTasks() excepts includes logging. * * @param tasks */ @Deprecated protected final void addDeleteAndLogTasks(TaskList tasks) { log(LogAction.Type.DELETE, tasks); addDeleteTasks(tasks); } private void log(LogAction.Type type, TaskList tasks) { WrapperLogProvider<E> logProvider = getLogProvider(); if (logProvider != null) { LogGroup logGroup = tasks.getLogGroup(); tasks.add(new LogAction<E>(type, this, logProvider, logGroup)); } } /** * return the list of the different properties we want to notify when we * call firePropertyChanges */ protected abstract List<Property<?, ? super E>> getProperties(); /** * When retrieve the values from the database, need to fire the * modifications for the different objects contained in the wrapped object */ private void firePropertyChanges(E oldWrappedObject, E newWrappedObject) { List<Property<?, ? super E>> properties = getProperties(); if (oldWrappedObject == newWrappedObject) { return; } for (Property<?, ? super E> property : properties) { String propertyName = property.getPropertyChangeName(); PropertyChangeListener[] listeners = propertyChangeSupport .getPropertyChangeListeners(propertyName); // if no one is listening to this property then do not send a change // as it may be expensive to determine the old and new values (ex: // lazily loading an association, such as, a Center's // specimenCollection). if (listeners.length == 0) { continue; } // don't fire a property change if the old model's property has not // even been initialized or loaded, as the old value is not // necessarily correct (if we lazily load it now, then it will if (!isInitialized(oldWrappedObject, property)) { continue; } Object oldValue = property.get(oldWrappedObject); Object newValue = property.get(newWrappedObject); // if the old and new property value are the same, do not send a // property change event if (oldValue == newValue || (oldValue != null && oldValue.equals(newValue))) { continue; } propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue); } } /** * using this wrapper id, retrieve the object from the database */ protected E getObjectFromDatabase() throws BiobankException { Integer id = null; List<E> list = null; try { id = getId(); DetachedCriteria c = DetachedCriteria.forClass(getWrappedClass()) .setResultTransformer( CriteriaSpecification.DISTINCT_ROOT_ENTITY) .add(Restrictions.idEq(id)); list = appService.query(c); } catch (Exception ex) { throw new BiobankException(ex); } if (list.size() == 0) return null; if (list.size() == 1) { return list.get(0); } throw new BiobankException(MessageFormat.format( "Found {0} objects of type {1} with id={2}", list.size(), //$NON-NLS-1$ getWrappedClass().getName(), id)); } public abstract Class<E> getWrappedClass(); public void reset() throws Exception { clear(); if (isNew()) { resetToNewObject(); } else { reload(); } } /** * even if this object was loaded form database, start form scratch (so * isNew = true) * * @throws Exception */ private void resetToNewObject() throws Exception { E oldWrappedObject = wrappedObject; wrappedObject = getNewObject(); session = new WrapperSession(this); firePropertyChanges(oldWrappedObject, wrappedObject); } private static final String PROPERTY_COUNT_HQL = "SELECT m.{0}.size FROM {1} m WHERE m.id = ?"; //$NON-NLS-1$ protected final <T> Long getPropertyCount( Property<Collection<T>, ? super E> property, boolean fast) throws BiobankQueryResultSizeException, ApplicationException { long count = 0; if (fast && !isInitialized(property)) { String prop = property.getName(); String klazz = getWrappedClass().getName(); String hql = MessageFormat.format(PROPERTY_COUNT_HQL, prop, klazz); List<Object> params = Arrays.asList(new Object[] { getId() }); HQLCriteria criteria = new HQLCriteria(hql, params); count = getCountResult(appService, criteria); } else { Collection<T> collection = property.get(wrappedObject); count = collection != null ? collection.size() : 0; } return count; } /** * Override this method to supply default values. * * @return * @throws Exception */ protected E getNewObject() throws Exception { // TODO: could override in base classes with "new WrappedClass();" then // no exception would be thrown. Constructor<E> constructor = getWrappedClass().getConstructor(); return constructor.newInstance(); } private static final String CHECK_NO_DUPLICATES = "select count(o) from {0} " //$NON-NLS-1$ + "as o where {1}=? {2}"; //$NON-NLS-1$ protected void checkNoDuplicates(Class<?> objectClass, String propertyName, String value, String errorName) throws ApplicationException, BiobankException { HQLCriteria c; final List<Object> params = new ArrayList<Object>(); params.add(value); String equalsTest = ""; //$NON-NLS-1$ if (!isNew()) { equalsTest = " and id <> ?"; //$NON-NLS-1$ params.add(getId()); } final String hqlString = MessageFormat.format(CHECK_NO_DUPLICATES, objectClass.getName(), propertyName, equalsTest); c = new HQLCriteria(hqlString, params); if (getCountResult(appService, c) > 0) { throw new DuplicateEntryException( errorName + " " //$NON-NLS-1$ + MessageFormat.format( Messages.getString("ModelWrapper.already.exist.msg"), value)); //$NON-NLS-1$ } } /** * The query should be a count query. The value returned is the result of * the count. */ // TODO: move this to some Util class somewhere else public static Long getCountResult(WritableApplicationService appService, HQLCriteria criteria) throws BiobankQueryResultSizeException, ApplicationException { List<Number> results = appService.query(criteria); if (results.size() != 1) { throw new BiobankQueryResultSizeException(); } return Long.valueOf(results.get(0).longValue()); } /** * return true if integrity of this object is ok */ public boolean checkIntegrity() { return true; } @Override public boolean equals(Object object) { if (object == null) { return false; } if (getClass() != object.getClass()) { return false; } Integer id = getId(); Integer id2 = ((ModelWrapper<?>) object).getId(); if (id == null && id2 == null) { return wrappedObject == ((ModelWrapper<?>) object).wrappedObject; } return id != null && id2 != null && id.equals(id2); } /** * Returns hash code for the primary key of the object **/ @Override public int hashCode() { if (getId() != null) return getId().hashCode(); return 0; } /** * If we want to reset internal fields when reload or reset is called (even * if the object is new). Please don't touch the wrapped object. */ protected void resetInternalFields() { // default do nothing } // TODO: switch to using the properties? @Override public String toString() { Class<E> classType = getWrappedClass(); if (classType != null) { StringBuffer sb = new StringBuffer(); Method[] methods = classType.getMethods(); for (Method method : methods) { String name = method.getName(); Class<?> returnType = method.getReturnType(); if (name.startsWith("get") //$NON-NLS-1$ && !name.equals("getClass") //$NON-NLS-1$ && (String.class.isAssignableFrom(returnType) || Number.class .isAssignableFrom(returnType))) { try { Object res = method.invoke(wrappedObject, (Object[]) null); if (res != null) { sb.append(name).append(":").append(res.toString()) //$NON-NLS-1$ .append("/"); //$NON-NLS-1$ } } catch (Exception e) { throw new RuntimeException("Error in toString method", //$NON-NLS-1$ e); } } } return sb.toString(); } return super.toString(); } /** * return true if the user can edit this object */ public boolean canUpdate(UserWrapper user, CenterWrapper<?> center, StudyWrapper study) { // try { // return user.hasPrivilegeOnKeyDesc( // PrivilegeWrapper.getUpdatePrivilege(appService), center, study, // getWrappedClass().getSimpleName()); // } catch (Exception e) { // throw new RuntimeException(e); // } return true; } /** * return true if the user can delete this object */ public boolean canDelete(UserWrapper user, CenterWrapper<?> center, StudyWrapper study) { // try { // return user.hasPrivilegeOnKeyDesc( // PrivilegeWrapper.getDeletePrivilege(appService), center, study, // getWrappedClass().getSimpleName()); // } catch (Exception e) { // throw new RuntimeException(e); // } return true; } public void addWrapperListener(WrapperListener listener) { listeners.add(listener); } public void removeWrapperListener(WrapperListener listener) { listeners.add(listener); } public void notifyListeners(WrapperEvent event) { // create a new list to avoid concurrent modification for (WrapperListener listener : new ArrayList<WrapperListener>( listeners)) { switch (event.getType()) { case UPDATE: listener.updated(event); break; case INSERT: listener.inserted(event); break; case DELETE: listener.deleted(event); break; } } } public void initObjectWith(ModelWrapper<E> otherWrapper) throws BiobankException { if (otherWrapper == null) { throw new BiobankCheckException( "Cannot init internal object with a null wrapper"); //$NON-NLS-1$ } setWrappedObject(otherWrapper.wrappedObject); } public void logLookup(String center) throws Exception { ((BiobankApplicationService) appService).logActivity(getLogMessage( "select", center, getWrappedClass().getSimpleName() + " LOOKUP")); //$NON-NLS-1$//$NON-NLS-2$ } public void logEdit(String site) throws Exception { if (!isNew()) { ((BiobankApplicationService) appService).logActivity(getLogMessage( "edit", site, getWrappedClass().getSimpleName() + " EDIT")); //$NON-NLS-1$ //$NON-NLS-2$ } } protected final Log getLogMessage(String action, String site, String details) throws Exception { Log log = null; WrapperLogProvider<E> logProvider = getLogProvider(); if (logProvider != null) { log = logProvider.getLog(getWrappedObject()); log.setAction(action); log.setCenter(site); log.setType(getWrappedClass().getSimpleName()); if (details != null) { if (log.getDetails() != null) { details += log.getDetails(); } log.setDetails(details); } } return log; } protected WrapperLogProvider<E> getLogProvider() { return null; } @Override public int compareTo(ModelWrapper<E> other) { return NullHelper.safeCompareTo(getId(), other.getId()); } public static <W extends ModelWrapper<? extends R>, R, M> List<W> wrapModelCollection( WritableApplicationService appService, Collection<? extends R> modelCollection, Class<W> wrapperKlazz) { List<W> wrappers = new ArrayList<W>(); if (modelCollection != null) { for (R element : modelCollection) { W wrapper = WrapperUtil.wrapModel(appService, element, wrapperKlazz); wrappers.add(wrapper); } } return wrappers; } protected <W extends ModelWrapper<? extends M>, M> W getWrappedProperty( Property<M, ? super E> property, Class<W> klazz) { @SuppressWarnings("unchecked") W wrapper = (W) recallProperty(property); if (wrapper == null && !isPropertyCached(property)) { wrapper = wrapProperty(property, klazz); cacheProperty(property, wrapper); } return wrapper; } private <W extends ModelWrapper<? extends M>, M> W wrapProperty( Property<M, ? super E> property, Class<W> klazz) { M model = property.get(wrappedObject); @SuppressWarnings("unchecked") W wrapper = (W) session.get(model); if (wrapper == null) { wrapper = WrapperUtil.wrapModel(appService, model, klazz); session.add(wrapper); } else { // if we're replacing the value with one from the cache, then make // sure that we set it on the underlying model object as well property.set(wrappedObject, wrapper.wrappedObject); } return wrapper; } protected <W extends ModelWrapper<? extends R>, R> void setWrappedProperty( Property<R, ? super E> property, W wrapper) { elementTracker.trackProperty(property); R newValue = wrapper != null ? wrapper.wrappedObject : null; setProperty(this, property, newValue, wrapper); } protected <W extends ModelWrapper<? extends R>, R> void setWrapperCollection( Property<Collection<R>, ? super E> property, Collection<W> wrappers) { Collection<R> newValues = new HashSet<R>(); for (W element : wrappers) { newValues.add(element.getWrappedObject()); } elementTracker.trackCollection(property); setModelProperty(this, property, newValues, wrappers); } private <W extends ModelWrapper<? extends M>, M> List<W> wrapCollectionProperty( Property<Collection<M>, ? super E> property, Class<W> klazz) { List<W> wrappers = new ArrayList<W>(); Collection<M> modelCollection = property.get(wrappedObject); if (modelCollection != null) { Set<M> newModels = new HashSet<M>(); for (M model : modelCollection) { @SuppressWarnings("unchecked") W wrapper = (W) session.get(model); if (wrapper == null) { wrapper = WrapperUtil.wrapModel(appService, model, klazz); session.add(wrapper); } // the wrapper might have come from the session so remember it // to re-set the value of this property later. newModels.add(wrapper.wrappedObject); wrappers.add(wrapper); } property.set(wrappedObject, newModels); } return wrappers; } protected <W extends ModelWrapper<? extends R>, R> List<W> getWrapperCollection( Property<Collection<R>, ? super E> property, Class<W> wrapperKlazz, boolean sort) { @SuppressWarnings("unchecked") List<W> wrappers = (List<W>) recallProperty(property); if (wrappers == null && !isPropertyCached(property)) { wrappers = wrapCollectionProperty(property, wrapperKlazz); // TODO: if an object or collection of objects is lazily loaded, if // there are any references between the objects, ALL references must // be replaced with the object in the session, if an object exists // in the session. This can be done later since it is probably // unlikely. But if not done, will yield incorrect results. cacheProperty(property, wrappers); elementQueue.flush(property); } if (wrappers != null && sort) { // TODO: should do this once per property? Collections.sort(wrappers); } // return a copy of the internally stored list so that someone // externally modifying the collection does not modify the internal // collection. if (wrappers != null) return new ArrayList<W>(wrappers); return new ArrayList<W>(); } protected <W extends ModelWrapper<? extends R>, R> void addToWrapperCollection( Property<Collection<R>, ? super E> property, List<W> newWrappers) { if (newWrappers == null || newWrappers.isEmpty()) { return; } // Use a set so that wrappers of the same object are not double-added. Set<W> allWrappers = new HashSet<W>(); @SuppressWarnings("unchecked") Class<W> wrapperKlazz = (Class<W>) newWrappers.get(0).getClass(); List<W> currentWrappers = getWrapperCollection(property, wrapperKlazz, false); // if the new wrapper is already in the collection, use the new one allWrappers.addAll(newWrappers); if (currentWrappers != null) { currentWrappers.removeAll(newWrappers); allWrappers.addAll(currentWrappers); } setWrapperCollection(property, new ArrayList<W>(allWrappers)); } protected <W extends ModelWrapper<? extends R>, R> void removeFromWrapperCollection( Property<Collection<R>, ? super E> property, List<W> wrappersToRemove) { if (wrappersToRemove == null || wrappersToRemove.isEmpty()) { return; } Collection<W> allWrappers = new ArrayList<W>(); @SuppressWarnings("unchecked") Class<W> wrapperKlazz = (Class<W>) wrappersToRemove.get(0).getClass(); List<W> currentWrappers = getWrapperCollection(property, wrapperKlazz, false); allWrappers.addAll(currentWrappers); allWrappers.removeAll(wrappersToRemove); setWrapperCollection(property, allWrappers); } public <W extends ModelWrapper<? extends R>, R> void removeFromWrapperCollectionWithCheck( Property<Collection<R>, ? super E> property, List<W> wrappersToRemove) throws BiobankCheckException { if (wrappersToRemove == null || wrappersToRemove.isEmpty()) { return; } @SuppressWarnings("unchecked") Class<W> wrapperKlazz = (Class<W>) wrappersToRemove.get(0).getClass(); List<W> currentWrappers = getWrapperCollection(property, wrapperKlazz, false); if (!currentWrappers.containsAll(wrappersToRemove)) { throw new BiobankCheckException("object not in list"); //$NON-NLS-1$ } removeFromWrapperCollection(property, wrappersToRemove); } protected <T> T getProperty(Property<T, ? super E> property) { return getProperty(this, property); } protected <T, M> T getProperty(ModelWrapper<M> modelWrapper, Property<T, ? super M> property) { if (modelWrapper == null) { return null; } @SuppressWarnings("unchecked") T value = (T) modelWrapper.recallProperty(property); if (value == null && !modelWrapper.isPropertyCached(property)) { value = property.get(modelWrapper.getWrappedObject()); modelWrapper.cacheProperty(property, value); } return value; } protected <T> void setProperty(Property<T, ? super E> property, T newValue) { setProperty(this, property, newValue, newValue); } private <T, M> void setProperty(ModelWrapper<M> modelWrapper, Property<T, ? super M> property, T newValue, Object valueToCache) { setModelProperty(modelWrapper, property, newValue, valueToCache); } /** * Determines whether the given property of the wrapped object has been * initialized. * * @param property of the wrapped object * @return true if the given {@code Property} has been initialized or if the * wrapped object is not a proxy (and therefore new), otherwise * false. */ protected boolean isInitialized(Property<?, ? super E> property) { return isNew() || isInitialized(wrappedObject, property); } /** * Determines whether the given property of the given model object has been * initialized (loaded). * * @param model object with the {@code property} * @param property of the wrapped object * @return true if the given {@code Property} has been initialized or if the * wrapped object is not a proxy (and therefore new), otherwise * false. */ private static <E> boolean isInitialized(E model, Property<?, ? super E> property) { E unproxiedModel = ProxyUtil.convertProxyToObject(model); return Hibernate.isPropertyInitialized(unproxiedModel, property.getName()) && Hibernate.isInitialized(property.get(unproxiedModel)); } private <T, M, R> void setModelProperty(ModelWrapper<M> modelWrapper, Property<T, ? super M> property, T newValue, R valueForCache) { M model = modelWrapper.getWrappedObject(); // TODO: whenever a property is set, the old value is retrieved from // the database (or memory if already loaded) to send the // information for the change to listeners. This should be changed // to either (1) only get if there are listeners or (2) remember all // old values so they can be removed (see cascade().persistAdded()). T oldValue = property.get(model); property.set(model, newValue); // need to add into cache before the firePropertyChange is called // because its will call the getters that refers to the cache modelWrapper.cacheProperty(property, valueForCache); propertyChangeSupport.firePropertyChange(property.getName(), oldValue, newValue); } private void cacheProperty(Property<?, ?> property, Object value) { propertyCache.put(property, value); } protected boolean isPropertyCached(Property<?, ? super E> property) { return propertyCache.containsKey(property); } protected Object recallProperty(Property<?, ?> property) { return propertyCache.get(property); } @SuppressWarnings("unchecked") public ModelWrapper<E> getDatabaseClone() throws Exception { ModelWrapper<E> wrapper = null; Constructor<?> c = getClass().getDeclaredConstructor( WritableApplicationService.class); Object[] arglist = new Object[] { appService }; wrapper = (ModelWrapper<E>) c.newInstance(arglist); wrapper.setId(getId()); wrapper.reload(); return wrapper; } /** * @return a list of center security should check for modifications * * is not used anymore for now. But we will need something like that * so I keep it until I reuse what subclasses are doing with it. */ @Deprecated public List<? extends CenterWrapper<?>> getSecuritySpecificCenters() { return Collections.emptyList(); } /** * Clear internal state, cached, and state-tracking objects. */ private void clear() { elementQueue.clear(); elementTracker.clear(); propertyCache.clear(); cache.clear(); session = new WrapperSession(this); resetInternalFields(); } public ElementTracker<E> getElementTracker() { return elementTracker; } protected ElementQueue<E> getElementQueue() { return elementQueue; } protected WrapperCascader<E> cascade() { return cascader; } protected WrapperChecker<E> check() { return preChecker; } /** * Will consider the date and not the time. */ // TODO: why is this in ModelWrapper? It's a DateUtil function. public static Date endOfDay(Date date) { Calendar c = Calendar.getInstance(); c.setTime(date); c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.MINUTE, 0); c.set(Calendar.MILLISECOND, 0); c.add(Calendar.DAY_OF_MONTH, 1); return c.getTime(); } /** * Remove time on this date to get time set to 00:00 */ // TODO: why is this in ModelWrapper? It's a DateUtil function. public static Date startOfDay(Date date) { Calendar c = Calendar.getInstance(); c.setTime(date); c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.MINUTE, 0); c.set(Calendar.MILLISECOND, 0); return c.getTime(); } }