001    package com.sptci.prevayler;
002    
003    import com.sptci.ReflectionUtility;
004    import com.sptci.prevayler.annotations.Index;
005    import com.sptci.prevayler.annotations.Indices;
006    
007    import java.lang.reflect.Field;
008    import java.util.Collection;
009    import java.util.LinkedHashSet;
010    
011    /**
012     * Abstracts all index management operations for the prevalent system.
013     *
014     * <p>&copy; Copyright 2008 <a href='http://sptci.com/' target='_top'>Sans Pareil Technologies, Inc.</a></p>
015     * @author Rakesh Vidyadharan 2008-05-23
016     * @version $Id: IndexSystem.java 22 2008-11-24 19:04:25Z sptrakesh $
017     */
018    abstract class IndexSystem extends StorageSystem
019    {
020      private static final long serialVersionUID = 1L;
021    
022      /**
023       * Check all unique constraints (including object id) for the specified
024       * prevalent object.
025       *
026       * @see #checkIndices
027       * @see #checkIndex
028       * @see #checkFields
029       * @param object The prevalent object to check.
030       * @throws com.sptci.prevayler.ConstraintException If unique constraints are violated.
031       * @throws com.sptci.prevayler.PrevalentException If errors are encountered while processing
032       *   the fields of the prevalent object.
033       */
034      protected void checkUnique( final PrevalentObject object )
035        throws PrevalentException
036      {
037        if ( object.getObjectId() != null )
038        {
039          final PrimaryStorage primaryStorage =
040            getPrimaryStorage( object.getClass() );
041          if ( primaryStorage.isStored( object ) )
042          {
043            throw new ConstraintException( object );
044          }
045        }
046    
047        checkIndices( object );
048        final Index index = object.getClass().getAnnotation( Index.class );
049        if ( index != null ) checkIndex( index, object );
050        checkFields( object );
051      }
052    
053      /**
054       * Check the {@link com.sptci.prevayler.annotations.Indices} annotations
055       * on the specified prevalent object and check unique constraints.
056       *
057       * @see #checkIndex
058       * @param object The prevalent object to check.
059       * @throws com.sptci.prevayler.ConstraintException If a unique constraint is
060       *   violated.
061       * @throws com.sptci.prevayler.PrevalentException If exceptions are
062       *   encountered while processing the fields of the prevalent object.
063       */
064      private void checkIndices( final PrevalentObject object )
065        throws PrevalentException
066      {
067        final Indices indices = object.getClass().getAnnotation( Indices.class );
068    
069        if ( indices != null )
070        {
071          for ( Index index : indices.value() )
072          {
073            checkIndex( index, object );
074          }
075        }
076      }
077    
078      /**
079       * Check the index specified at the class level on the prevalent object.
080       *
081       * @param index The annotation for the prevalent object.
082       * @param object The prevalent object to check.
083       * @throws com.sptci.prevayler.ConstraintException If a unique constraint
084       *   is violated.
085       * @throws com.sptci.prevayler.PrevalentException If exceptions are
086       *   encountered while processing the fields of the prevalent object.
087       */
088      @SuppressWarnings( {"unchecked"} )
089      private void checkIndex( Index index, final PrevalentObject object )
090        throws PrevalentException
091      {
092        final IndexStorage indexStorage = getIndexStorage( object.getClass() );
093    
094        if ( index.unique() )
095        {
096          final StringBuilder builder = new StringBuilder( 64 );
097          final Collection collection = new LinkedHashSet( index.members().length );
098    
099          for ( String name : index.members() )
100          {
101            builder.append( name ).append( "#" );
102    
103            try
104            {
105              collection.add( ReflectionUtility.fetchObject( name, object ) );
106            }
107            catch ( Throwable t )
108            {
109              throw new PrevalentException( t );
110            }
111          }
112    
113          if ( indexStorage.isIndexed( index.members(), collection ) )
114          {
115            throw new ConstraintException( object,
116                builder.toString().replace( "#", "," ) );
117          }
118        }
119      }
120    
121      /**
122       * Check all the fields in the prevalent object for {@link
123       * com.sptci.prevayler.annotations.Index} annotation and check for
124       * unique constraint violations.
125       *
126       * @param object The prevalent object to check.
127       * @throws com.sptci.prevayler.ConstraintException If a unique constraint
128       *   is violated.
129       * @throws com.sptci.prevayler.PrevalentException If exceptions are
130       *   encountered while processing the fields of the prevalent object.
131       */
132      protected void checkFields( final PrevalentObject object )
133        throws PrevalentException
134      {
135        final IndexStorage indexStorage = getIndexStorage( object.getClass() );
136    
137        for ( Field field : ReflectionUtility.fetchFields( object ).values() )
138        {
139          final Index index = field.getAnnotation( Index.class );
140          if ( index != null )
141          {
142            if ( index.unique() )
143            {
144              Object value;
145    
146              try
147              {
148                value =
149                    ReflectionUtility.fetchObject( field.getName(), object );
150              }
151              catch ( Throwable t )
152              {
153                throw new PrevalentException( t );
154              }
155    
156              if ( indexStorage.isIndexed( field.getName(), value ) )
157              {
158                throw new ConstraintException( object, field.getName() );
159              }
160            }
161          }
162        }
163      }
164    
165      /**
166       * Manage additional maps required to support qeries on the prevalent
167       * object.
168       *
169       * @see #indexFields
170       * @see #indexClass
171       * @param object The prevalent object to add query support for.
172       * @throws com.sptci.prevayler.PrevalentException If errors are encountered
173       *   while fetching the values of the fields in prevalent object.
174       */
175      protected void index( final PrevalentObject object ) throws PrevalentException
176      {
177        indexFields( object );
178        indexClass( object );
179      }
180    
181      /**
182       * Process {@link com.sptci.prevayler.annotations.Index} annotations on the
183       * fields of the specified prevalent object and manage the {@link
184       * StorageSystem#indexMap} as appropriate.
185       *
186       * @param object The prevalent object to index.
187       * @throws com.sptci.prevayler.PrevalentException If errors are encountered
188       *   while processing the object fields.
189       */
190      protected void indexFields( final PrevalentObject object )
191        throws PrevalentException
192      {
193        final IndexStorage indexStorage = getIndexStorage( object.getClass() );
194    
195        try
196        {
197          for ( Field field : ReflectionUtility.fetchFields( object ).values() )
198          {
199            final Index index = field.getAnnotation( Index.class );
200    
201            if ( index != null )
202            {
203              final Object value = field.get( object );
204              indexStorage.add( field.getName(), value, object );
205            }
206          }
207        }
208        catch ( Throwable t )
209        {
210          throw new PrevalentException( t );
211        }
212      }
213    
214      /**
215       * Process the index annotations on the prevalent object and manage the
216       * {@link StorageSystem#indexMap} as appropriate.
217       *
218       * @see #processIndices
219       * @see #processIndex
220       * @param object The prevalent object to process.
221       * @throws com.sptci.prevayler.PrevalentException If errors are encountered
222       *   while processing the field values of the prevalent object.
223       */
224      protected void indexClass( final PrevalentObject object )
225        throws PrevalentException
226      {
227        processIndices( object );
228    
229        final Index index = object.getClass().getAnnotation( Index.class );
230        if ( index != null ) processIndex( index, object );
231      }
232    
233      /**
234       * Process the {@link com.sptci.prevayler.annotations.Indices} annotation
235       * and manage the {@link StorageSystem#indexMap} as appropriate.
236       *
237       * @see #processIndex
238       * @param object The prevalent object whose class level index annotations
239       *   are to be processed.
240       * @throws com.sptci.prevayler.PrevalentException If errors are encountered
241       *   while processing the fields of the prevalent object.
242       */
243      private void processIndices( final PrevalentObject object )
244        throws PrevalentException
245      {
246        final Indices indices = object.getClass().getAnnotation( Indices.class );
247        if ( indices != null )
248        {
249          for ( Index index : indices.value() )
250          {
251            processIndex( index, object );
252          }
253        }
254      }
255    
256      /**
257       * Process the {@link com.sptci.prevayler.annotations.Index} annotation
258       * at the class level of the specified prevalent object.
259       *
260       * @param index The annotation to process.
261       * @param object The prevalent object to process.
262       * @throws com.sptci.prevayler.PrevalentException If errors are encountered while processing
263       *   the fields in the prevalent object.
264       */
265      @SuppressWarnings( {"unchecked"} )
266      private void processIndex( final Index index,
267          final PrevalentObject object ) throws PrevalentException
268      {
269        final IndexStorage indexStorage = getIndexStorage( object.getClass() );
270    
271        try
272        {
273          final Collection collection = new LinkedHashSet( index.members().length );
274    
275          for ( String name : index.members() )
276          {
277            collection.add( ReflectionUtility.fetchObject( name, object ) );
278          }
279    
280          indexStorage.add( index.members(), collection, object );
281        }
282        catch ( Throwable t )
283        {
284          throw new PrevalentException( t );
285        }
286      }
287    
288      /**
289       * Remove the index for the specified prevalent object.  This is typically
290       * called prior to deleting a prevalent object.
291       *
292       * @param object The prevalent object to de-index.
293       * @throws PrevalentException If errors are encountered while removing the
294       *   index for the prevalent object.
295       */
296      protected void remove( final PrevalentObject object )
297          throws PrevalentException
298      {
299        final IndexStorage indexStorage = getIndexStorage( object.getClass() );
300        indexStorage.remove( object );
301      }
302    }