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>© 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 }