001/*
002        $Id: BasicItem.java 3207 2009-04-09 06:48:11Z gregory $
003
004        Copyright (C) 2006 Gregory Vincic, Olle Mansson
005        Copyright (C) 2007 Gregory Vincic
006
007        This file is part of Proteios.
008        Available at http://www.proteios.org/
009
010        Proteios is free software; you can redistribute it and/or modify it
011        under the terms of the GNU General Public License as published by
012        the Free Software Foundation; either version 2 of the License, or
013        (at your option) any later version.
014
015        Proteios is distributed in the hope that it will be useful, but
016        WITHOUT ANY WARRANTY; without even the implied warranty of
017        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
018        General Public License for more details.
019
020        You should have received a copy of the GNU General Public License
021        along with this program; if not, write to the Free Software
022        Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
023        02111-1307, USA.
024 */
025package org.proteios.core;
026
027import org.proteios.core.data.BasicData;
028
029import java.lang.ref.WeakReference;
030import java.util.Set;
031
032/**
033 * This is the root superclass of all item classes. All items must inherit from
034 * this class. This class provides access to the id, type and version of the
035 * item as well as permission checking methods.
036 * 
037 * @author Nicklas
038 * @version 2.0
039 * @base.modified $Date: 2009-04-09 08:48:11 +0200 (Thu, 09 Apr 2009) $
040 */
041public abstract class BasicItem<D extends BasicData>
042                implements Identifiable
043{
044        /**
045         * The data class from the org.proteios.core.data layer that holds the data
046         * for this item.
047         */
048        private final D data;
049        /**
050         * A reference to the current DbControl object. We use a WeakReference since
051         * we it is always the client applications responsibility to keep control
052         * over the DbControl object.
053         */
054        private WeakReference<DbControl> dc;
055        /**
056         * The logged in user's permission to this item. Calculated by the
057         * {@link #initPermissions(int, int)} method.
058         */
059        private int permissions;
060
061
062        /**
063         * Create a new instance. Do not call this directly. Use the
064         * {@link DbControl#newItem(Class, Object[])} or
065         * {@link DbControl#getItem(Class, BasicData, Object[])} methods instead.
066         */
067        BasicItem(D data)
068        {
069                this.data = data;
070                this.permissions = 127; // All item permissions
071        }
072
073
074        /*
075         * From the AccessControlled interface
076         * -------------------------------------------
077         */
078        /**
079         * Checks if the logged in user has the specified permission on this item.
080         * The default implementation only checks the role keys for permissions. A
081         * subclass may not override this method since it would make it possible to
082         * bypass security checks.
083         * <p>
084         * Subclasses that needs to check other keys, such as the {@link SharedItem}
085         * should override the {@link #initPermissions(int,int)} method instead.
086         * 
087         * @param permission A value from the {@link Permission} class
088         * @return <code>TRUE</code> if the user has the permission,
089         *         <code>FALSE</code> otherwise
090         */
091        public final boolean hasPermission(Permission permission)
092        {
093                return Permission.hasPermission(permissions, permission);
094        }
095
096
097        /**
098         * Checks if the logged in user has the specified permission on this item.
099         * If not, a {@link PermissionDeniedException} is thrown.
100         * 
101         * @param permission A value from the {@link Permission} class
102         * @throws PermissionDeniedException If the logged in user doesn't have the
103         *         requested permission
104         */
105        public final void checkPermission(Permission permission)
106                        throws PermissionDeniedException
107        {
108                if (!hasPermission(permission))
109                {
110                        throw new PermissionDeniedException(permission, this.toString());
111                }
112        }
113
114
115        public final Set<Permission> getPermissions()
116        {
117                return Permission.fromInt(permissions);
118        }
119
120
121        // -------------------------------------------
122        /*
123         * From the Identifiable interface
124         * -------------------------------------------
125         */
126        public final int getId()
127        {
128                return data.getId();
129        }
130
131
132        public final int getVersion()
133        {
134                return data.getVersion();
135        }
136
137
138        // The getType() method must be implemented by each subclass
139        // -------------------------------------------
140        /*
141         * From the Object class -------------------------------------------
142         */
143        @Override
144        public String toString()
145        {
146                String id = getId() == 0 ? "new" : "id=" + getId();
147                if (this instanceof Nameable)
148                {
149                        Nameable n = (Nameable) this;
150                        id += "; name=" + n.getName();
151                }
152                return getType().toString() + "[" + id + "]";
153        }
154
155
156        /**
157         * Check if this item is equal to another item. They are considered to be
158         * equal if their BasicData objects are equal.
159         * 
160         * @see BasicData#equals(Object)
161         */
162        @Override
163        public final boolean equals(Object o)
164        {
165                if (this == o)
166                        return true;
167                if (!(o instanceof BasicItem))
168                        return false;
169                BasicItem item = (BasicItem) o;
170                return data.equals(item.data);
171        }
172
173
174        /**
175         * The hash code is calculated at object construction and remains the same
176         * during the objects lifetime. For new items the hash code is randomly
177         * generated. Items loaded from the database calculates the hash code from
178         * the from the id.
179         * 
180         * @see BasicData#hashCode()
181         */
182        @Override
183        public final int hashCode()
184        {
185                return data.hashCode();
186        }
187
188
189        // -------------------------------------------
190        /**
191         * Checks if the item has been saved to the database or not.
192         * 
193         * @return TRUE if the item is saved to the database, FALSE otherwise
194         */
195        public final boolean isInDatabase()
196        {
197                return getId() != 0;
198        }
199
200
201        /**
202         * Check if this item is attached to a {@link DbControl} object or not. An
203         * item is detached if there is no <code>DbControl</code>, if it is
204         * closed, or if is not managed by the <code>DbControl</code>.
205         * 
206         * @return TRUE if the item is detached, FALSE otherwise
207         * @see DbControl#detachItem(BasicItem)
208         * @see DbControl#reattachItem(BasicItem)
209         * @see DbControl#saveItem(BasicItem)
210         */
211        public final boolean isDetached()
212        {
213                return dc == null || dc.get() == null || dc.get().isClosed() || !dc
214                        .get().isAttached(this);
215        }
216
217
218        /**
219         * Check if this item is used by some other item. With used we mean that
220         * another item is linking to this item in way that prevents this item from
221         * beeing deleted. Ie. if we tried to delete an item that is used, we would
222         * get a foreign key violation error from the database.
223         * 
224         * @return TRUE if this item is used, FALSE otherwise
225         */
226        public abstract boolean isUsed()
227                        throws BaseException;
228
229
230        /**
231         * Get the {@link BasicData} object that holds all data for this item.
232         */
233        final D getData()
234        {
235                return data;
236        }
237
238
239        /**
240         * Get the {@link DbControl} object that currently manages this item.
241         * 
242         * @throws ConnectionClosedException If the item is connected to a closed
243         *         <code>DbControl</code> object, or not connected at all
244         */
245        public final DbControl getDbControl()
246                        throws ConnectionClosedException
247        {
248                DbControl dbControl = dc != null ? dc.get() : null;
249                if (dbControl == null)
250                        throw new ConnectionClosedException();
251                return dbControl;
252        }
253
254
255        /**
256         * Set the {@link DbControl} object that should manage this item. A null
257         * value detaches the item
258         */
259        final void setDbControl(DbControl dbControl)
260        {
261                dc = dbControl == null ? null : new WeakReference<DbControl>(dbControl);
262        }
263
264
265        /**
266         * Get the {@link SessionControl} object that manages this item.
267         * 
268         * @throws ConnectionClosedException If the item is connected to a closed
269         *         <code>DbControl</code> object, or not connected at all
270         */
271        public final SessionControl getSessionControl()
272                        throws ConnectionClosedException
273        {
274                return getDbControl().getSessionControl();
275        }
276
277
278        /**
279         * Initialise the logged in user's permissions for this item. For items
280         * loaded from the database, the default implementation checks the role
281         * keys. For new items, write permission is added.
282         * <p>
283         * Subclasses that needs to check other keys or properties, such as the
284         * {@link OwnedItem} and {@link SharedItem} should override this method. The
285         * subclass should calculate additional permissions to be granted or denied,
286         * and combine those with whatever was passed as parameters. Use the binary
287         * OR operator ( | ) to combine the permissions. Finally the subclass must
288         * call <code>super.initPermissions(granted, denied)</code>.
289         * 
290         * @param granted Permissions that have been granted by the subclass
291         * @param denied Permissions that have been denied by the subclass
292         * @throws BaseException If the permissions couldn't be initialised
293         */
294        void initPermissions(int granted, int denied)
295                        throws BaseException
296        {
297                int rolePermissions = getSessionControl().getRolePermissions(getType());
298                granted |= rolePermissions;
299                if (!isInDatabase())
300                {
301                        granted |= Permission.grant(Permission.WRITE);
302                }
303                this.permissions = granted & ~denied; // granted AND NOT denied
304        }
305
306
307        /**
308         * This method is called on each {@link Validatable} item before is saved to
309         * the database. If the subclass overrides this method it should also
310         * propagate the call to the superclass, ie. <code>super.validate()</code>.
311         * 
312         * @throws BaseException If there is an error
313         * @see Validatable
314         * @see <a
315         *      href="../../../../../../development/overview/core/datavalidation.html">Core
316         *      API overview - Data validation</a>
317         * @see <a
318         *      href="../../../../../../development/coding/item/index.html#validation">Coding
319         *      rules and guidelines for item classes</a>
320         */
321        void validate()
322                        throws InvalidDataException, BaseException
323        {}
324
325
326        /**
327         * This method is called on each {@link Transactional} item and on all items
328         * if the action is {@link Transactional.Action#CREATE} or
329         * {@link Transactional.Action#DELETE} before a commit is issued to the
330         * database. If the subclass overrides this method it should also propagate
331         * the call to the superclass, ie. <code>super.onBeforeCommit(action)</code>.
332         * 
333         * @throws BaseException If there is an error
334         * @see Transactional
335         * @see <a
336         *      href="../../../../../../development/overview/core/transactions.html">Core
337         *      API overview - Transaction handling</a>
338         * @see <a
339         *      href="../../../../../../development/coding/item/index.html#transactions">Coding
340         *      rules and guidelines for item classes</a>
341         */
342        void onBeforeCommit(Transactional.Action action)
343                        throws BaseException
344        {}
345
346
347        /**
348         * This method is called on all items directly after Hibernate has inserted
349         * it into the database. This method can be used in place of the
350         * {@link #onBeforeCommit(Transactional.Action)} in case the id is needed.
351         * The id has not been generated when the <code>onBeforeCommit</code> is
352         * called.
353         * 
354         * @throws BaseException If there is an error
355         * @see Transactional
356         * @see <a
357         *      href="../../../../../../development/overview/core/transactions.html">Core
358         *      API overview - Transaction handling</a>
359         * @see <a
360         *      href="../../../../../../development/coding/item/index.html#transactions">Coding
361         *      rules and guidelines for item classes</a>
362         */
363        void onAfterInsert()
364                        throws BaseException
365        {}
366
367
368        /**
369         * This method is called on each {@link Transactional} object after a
370         * successful commit to the database. If the subclass overrides this method
371         * it should also propagate the call to the superclass, ie.
372         * <code>super.onAfterCommit(action)</code>. It is forbidden to access
373         * the <code>DbControl</code> object from this method and it must not
374         * throw any exceptions. All exceptions should be logged using the
375         * {@link Application#getLogger()} object.
376         * 
377         * @see Transactional
378         * @see <a
379         *      href="../../../../../../development/overview/core/transactions.html">Core
380         *      API overview - Transaction handling</a>
381         * @see <a
382         *      href="../../../../../../development/coding/item/index.html#transactions">Coding
383         *      rules and guidelines for item classes</a>
384         */
385        void onAfterCommit(Transactional.Action action)
386        {}
387
388
389        /**
390         * This method is called on each {@link Transactional} object after an
391         * unsuccessful commit to the database. If the subclass overrides this
392         * method it should also propagate the call to the superclass, ie.
393         * <code>super.onRollback(action)</code>. It is forbidden to access the
394         * <code>DbControl</code> object from this method and it must not throw
395         * any exceptions. All exceptions should be logged using the
396         * {@link Application#getLogger()} object.
397         * 
398         * @see Transactional
399         * @see <a
400         *      href="../../../../../../development/overview/core/transactions.html">Core
401         *      API overview - Transaction handling</a>
402         * @see <a
403         *      href="../../../../../../development/coding/item/index.html#transactions">Coding
404         *      rules and guidelines for item classes</a>
405         */
406        void onRollback(Transactional.Action action)
407        {}
408}