1 /* 
  2  * Created on Sep 13, 2004 
  3  * 
  4  */ 
  5 package ubc.midp.mobilephoto.core.ui.datamodel; 
  6  
  7 import java.util.Vector; 
  8  
  9 import javax.microedition.lcdui.Image; 
 10 import javax.microedition.rms.RecordEnumeration; 
 11 import javax.microedition.rms.RecordStore; 
 12 import javax.microedition.rms.RecordStoreException; 
 13 import javax.microedition.rms.RecordStoreNotOpenException; 
 14  
 15 import lancs.midp.mobilephoto.lib.exceptions.ImageNotFoundException; 
 16 import lancs.midp.mobilephoto.lib.exceptions.ImagePathNotValidException; 
 17 import lancs.midp.mobilephoto.lib.exceptions.InvalidImageDataException; 
 18 import lancs.midp.mobilephoto.lib.exceptions.InvalidImageFormatException; 
 19 import lancs.midp.mobilephoto.lib.exceptions.InvalidPhotoAlbumNameException; 
 20 import lancs.midp.mobilephoto.lib.exceptions.NullAlbumDataReference; 
 21 import lancs.midp.mobilephoto.lib.exceptions.PersistenceMechanismException; 
 22 import ubc.midp.mobilephoto.core.util.ImageUtil; 
 23  
 24 /** 
 25  * @author trevor 
 26  *  
 27  * This is the main data access class. It handles all the connectivity with the 
 28  * RMS record stores to fetch and save data associated with MobilePhoto TODO: 
 29  * Refactor into stable interface for future updates. We may want to access data 
 30  * from RMS, or eventually direct from the 'file system' on devices that support 
 31  * the FileConnection optional API. 
 32  *  
 33  */ 
 34 public class ImageAccessor { 
 35  
 36   // Note: Our midlet only ever has access to Record Stores it created 
 37   // For now, use naming convention to create record stores used by 
 38   // MobilePhoto 
 39   public static final String ALBUM_LABEL = "mpa-"; // "mpa- all album names 
 40                             // are prefixed with 
 41                             // this label 
 42   public static final String INFO_LABEL = "mpi-"; // "mpi- all album info 
 43                           // stores are prefixed with 
 44                           // this label 
 45   public static final String DEFAULT_ALBUM_NAME = "My Photo Album"; // default 
 46                                     // album 
 47                                     // name 
 48  
 49   public static final String IMAGE_LABEL = "ImageList"; // RecordStore name 
 50                               // prefixed 
 51  
 52   protected String[] albumNames; // User defined names of photo albums 
 53  
 54   protected AlbumData model; 
 55  
 56   // Record Stores 
 57   private RecordStore imageRS = null; 
 58   private RecordStore imageInfoRS = null; 
 59  
 60   /* 
 61    * Constructor 
 62    */ 
 63   public ImageAccessor(AlbumData mod) { 
 64     model = mod; 
 65   } 
 66  
 67   /** 
 68    * Load all existing photo albums that are defined in the record store. 
 69    *  
 70  
 71    * @throws InvalidImageDataException 
 72    * @throws PersistenceMechanismException 
 73    */ 
 74   public void loadAlbums() throws InvalidImageDataException, 
 75       PersistenceMechanismException { 
 76  
 77     // Try to find any existing Albums (record stores) 
 78  
 79     String[] currentStores = RecordStore.listRecordStores(); 
 80  
 81     if (currentStores != null) { 
 82       System.out.println("ImageAccessor::loadAlbums: Found: " + currentStores.length + " existing record stores"); 
 83       model.existingRecords = true; 
 84       String[] temp = new String[currentStores.length]; 
 85       int count = 0; 
 86  
 87       // Only use record stores that follow the naming convention defined 
 88       for (int i = 0; i < currentStores.length; i++) { 
 89         String curr = currentStores[i]; 
 90  
 91         // If this record store is a photo album... 
 92         if (curr.startsWith(ALBUM_LABEL)) { 
 93  
 94           // Strip out the mpa- identifier 
 95           curr = curr.substring(4); 
 96           // Add the album name to the array 
 97           temp[i] = curr; 
 98           count++; 
 99         } 
100       } 
101  
102       // Re-copy the contents into a smaller array now that we know the 
103       // size 
104       albumNames = new String[count]; 
105       int count2 = 0; 
106       for (int i = 0; i < temp.length; i++) { 
107         if (temp[i] != null) { 
108           albumNames[count2] = temp[i]; 
109           count2++; 
110         } 
111       } 
112     } else { 
113       System.out.println("ImageAccessor::loadAlbums: 0 record stores exist. Creating default one."); 
114       resetImageRecordStore(); 
115       loadAlbums(); 
116     } 
117 //    System.out.println("ImageAccessor::loadAlbums: Finished ok! "); 
118   } 
119  
120   /** 
121    * Reset the album data for MobilePhoto. This will delete all existing photo 
122    * data from the record store and re-create the default album and photos. 
123    *  
124    * @throws InvalidImageFormatException 
125    * @throws ImagePathNotValidException 
126    * @throws InvalidImageDataException 
127    * @throws PersistenceMechanismException 
128    *  
129    */ 
130   public void resetImageRecordStore() throws InvalidImageDataException, PersistenceMechanismException { 
131  
132     String storeName = null; 
133     String infoStoreName = null; 
134  
135     // remove any existing album stores... 
136     if (albumNames != null) { 
137       for (int i = 0; i < albumNames.length; i++) { 
138         try { 
139           // Delete all existing stores containing Image objects as 
140           // well as the associated ImageInfo objects 
141           // Add the prefixes labels to the info store 
142  
143           storeName = ALBUM_LABEL + albumNames[i]; 
144           infoStoreName = INFO_LABEL + albumNames[i]; 
145  
146           System.out.println("<* ImageAccessor.resetImageRecordStore() *> delete "+storeName); 
147            
148           RecordStore.deleteRecordStore(storeName); 
149           RecordStore.deleteRecordStore(infoStoreName); 
150  
151         } catch (RecordStoreException e) { 
152           System.out.println("No record store named " + storeName 
153               + " to delete."); 
154           System.out.println("...or...No record store named " 
155               + infoStoreName + " to delete."); 
156           System.out.println("Ignoring Exception: " + e); 
157           // ignore any errors... 
158         } 
159       } 
160     } else { 
161       // Do nothing for now 
162       System.out 
163           .println("ImageAccessor::resetImageRecordStore: albumNames array was null. Nothing to delete."); 
164     } 
165  
166     // Now, create a new default album for testing 
167     addImageData("Tucan Sam", "/images/Tucan.png", 
168         ImageAccessor.DEFAULT_ALBUM_NAME); 
169     // Add Penguin 
170     addImageData("Linux Penguin", "/images/Penguin.png", 
171         ImageAccessor.DEFAULT_ALBUM_NAME); 
172     // Add Duke 
173     addImageData("Duke (Sun)", "/images/Duke1.PNG", 
174         ImageAccessor.DEFAULT_ALBUM_NAME); 
175     addImageData("UBC Logo", "/images/ubcLogo.PNG", 
176         ImageAccessor.DEFAULT_ALBUM_NAME); 
177     // Add Gail 
178     addImageData("Gail", "/images/Gail1.PNG", 
179         ImageAccessor.DEFAULT_ALBUM_NAME); 
180     // Add JG 
181     addImageData("J. Gosling", "/images/Gosling1.PNG", 
182         ImageAccessor.DEFAULT_ALBUM_NAME); 
183     // Add GK 
184     addImageData("Gregor", "/images/Gregor1.PNG", 
185         ImageAccessor.DEFAULT_ALBUM_NAME); 
186     // Add KDV 
187     addImageData("Kris", "/images/Kdvolder1.PNG", 
188         ImageAccessor.DEFAULT_ALBUM_NAME); 
189  
190   } 
191  
192   public void addImageData(String photoname, String path, String albumname) 
193       throws InvalidImageDataException, PersistenceMechanismException { 
194  
195     try { 
196       imageRS = RecordStore 
197           .openRecordStore(ALBUM_LABEL + albumname, true); 
198       imageInfoRS = RecordStore.openRecordStore(INFO_LABEL + albumname, 
199           true); 
200  
201       int rid; // new record ID for Image (bytes) 
202       int rid2; // new record ID for ImageData (metadata) 
203  
204       ImageUtil converter = new ImageUtil(); 
205  
206       // NOTE: For some Siemen's phone, all images have to be less than 
207       // 16K 
208       // May have to check for this, or try to convert to a lesser format 
209       // for display on Siemen's phones (Could put this in an Aspect) 
210  
211       // Add Tucan 
212       byte[] data1 = converter.readImageAsByteArray(path); 
213       rid = imageRS.addRecord(data1, 0, data1.length); 
214       ImageData ii = new ImageData(rid, ImageAccessor.ALBUM_LABEL 
215           + albumname, photoname); 
216       rid2 = imageInfoRS.getNextRecordID(); 
217       ii.setRecordId(rid2); 
218       data1 = converter.getBytesFromImageInfo(ii); 
219       imageInfoRS.addRecord(data1, 0, data1.length); 
220  
221       imageRS.closeRecordStore(); 
222  
223       imageInfoRS.closeRecordStore(); 
224     } catch (RecordStoreException e) { 
225       throw new PersistenceMechanismException(); 
226     } 
227   } 
228   // #ifdef includeSmsFeature 
229   public byte[] getByteFromImage(Image img){ 
230     int w = img.getWidth(); 
231     int h = img.getHeight(); 
232     int data_int[] = new int[ w * h ]; 
233 //    img.getRGB( data_int, 0, w, 0, 0, w, h ); 
234     byte[] data_byte = new byte[ w * h * 3 ]; 
235     for ( int i = 0; i < w * h; ++i ) 
236     { 
237     int color = data_int[ i ]; 
238     int offset = i * 3; 
239     data_byte[ offset ] = ( byte ) ( ( color & 0xff0000 ) >> 16 ); 
240     data_byte[ offset + 
241     1 ] = ( byte ) ( ( color & 0xff00 ) >> 8 ); 
242     data_byte[ offset + 2 ] = ( byte ) ( ( color & 0xff ) ); 
243     } 
244     return data_byte; 
245   } 
246    
247    
248   public void addImageData(String photoname, Image imgdata, String albumname) 
249       throws InvalidImageDataException, PersistenceMechanismException { 
250  
251     try { 
252       imageRS = RecordStore 
253           .openRecordStore(ALBUM_LABEL + albumname, true); 
254       imageInfoRS = RecordStore.openRecordStore(INFO_LABEL + albumname, 
255           true); 
256       int rid; // new record ID for Image (bytes) 
257       int rid2; // new record ID for ImageData (metadata) 
258        
259        
260  
261       ImageUtil converter = new ImageUtil(); 
262  
263       // NOTE: For some Siemen's phone, all images have to be less than 
264       // 16K 
265       // May have to check for this, or try to convert to a lesser format 
266       // for display on Siemen's phones (Could put this in an Aspect) 
267  
268       // Add Tucan 
269       byte[] data1 = getByteFromImage(imgdata); 
270       rid = imageRS.addRecord(data1, 0, data1.length); 
271       ImageData ii = new ImageData(rid, ImageAccessor.ALBUM_LABEL 
272           + albumname, photoname); 
273       rid2 = imageInfoRS.getNextRecordID(); 
274       ii.setRecordId(rid2); 
275       data1 = converter.getBytesFromImageInfo(ii); 
276       imageInfoRS.addRecord(data1, 0, data1.length); 
277  
278       imageRS.closeRecordStore(); 
279  
280       imageInfoRS.closeRecordStore(); 
281     } catch (RecordStoreException e) { 
282       throw new PersistenceMechanismException(); 
283     } 
284   } 
285   //#endif   
286    
287    
288   // #ifdef includeCopyPhoto 
289   /** 
290    * @param photoname 
291    * @param imageData 
292    * @param albumname 
293    * @throws InvalidImageDataException 
294    * @throws PersistenceMechanismException 
295    */ 
296   public void addImageData(String photoname, ImageData imageData, String albumname) throws InvalidImageDataException, PersistenceMechanismException { 
297     try { 
298       imageRS = RecordStore.openRecordStore(ALBUM_LABEL + albumname, true); 
299       imageInfoRS = RecordStore.openRecordStore(INFO_LABEL + albumname, true); 
300       int rid2; // new record ID for ImageData (metadata) 
301       ImageUtil converter = new ImageUtil(); 
302       rid2 = imageInfoRS.getNextRecordID(); 
303       imageData.setRecordId(rid2); 
304       byte[] data1 = converter.getBytesFromImageInfo(imageData); 
305       imageInfoRS.addRecord(data1, 0, data1.length); 
306     } catch (RecordStoreException e) { 
307       throw new PersistenceMechanismException(); 
308     }finally{ 
309       try { 
310         imageRS.closeRecordStore(); 
311         imageInfoRS.closeRecordStore(); 
312       } catch (RecordStoreNotOpenException e) { 
313         // TODO Auto-generated catch block 
314         e.printStackTrace(); 
315       } catch (RecordStoreException e) { 
316         // TODO Auto-generated catch block 
317         e.printStackTrace(); 
318       } 
319     } 
320   } 
321   // #endif 
322  
323   /** 
324    * This will populate the imageInfo hashtable with the ImageInfo object, 
325    * referenced by label name and populate the imageTable hashtable with Image 
326    * objects referenced by the RMS record Id 
327    *  
328    * @throws PersistenceMechanismException 
329    */ 
330   public ImageData[] loadImageDataFromRMS(String recordName) 
331       throws PersistenceMechanismException, InvalidImageDataException { 
332  
333     Vector imagesVector = new Vector(); 
334  
335     try { 
336  
337 // [EF] not used      String storeName = ImageAccessor.ALBUM_LABEL + recordName; 
338       String infoStoreName = ImageAccessor.INFO_LABEL + recordName; 
339  
340       RecordStore infoStore = RecordStore.openRecordStore(infoStoreName, 
341           false); 
342       RecordEnumeration isEnum = infoStore.enumerateRecords(null, null, 
343           false); 
344  
345       while (isEnum.hasNextElement()) { 
346         // Get next record 
347         int currentId = isEnum.nextRecordId(); 
348         byte[] data = infoStore.getRecord(currentId); 
349  
350         // Convert the data from a byte array into our ImageData 
351         // (metadata) object 
352         ImageUtil converter = new ImageUtil(); 
353         ImageData iiObject = converter.getImageInfoFromBytes(data); 
354  
355         // Add the info to the metadata hashtable 
356         String label = iiObject.getImageLabel(); 
357         imagesVector.addElement(iiObject); 
358         model.getImageInfoTable().put(label, iiObject); 
359  
360       } 
361  
362       infoStore.closeRecordStore(); 
363  
364     }catch (RecordStoreException rse) { 
365       throw new PersistenceMechanismException(rse); 
366     } 
367  
368     // Re-copy the contents into a smaller array 
369     ImageData[] labelArray = new ImageData[imagesVector.size()]; 
370     imagesVector.copyInto(labelArray); 
371     return labelArray; 
372   } 
373  
374   /** 
375    * Update the Image metadata associated with this named photo 
376    * @throws InvalidImageDataException  
377    * @throws PersistenceMechanismException  
378    */ 
379   public boolean updateImageInfo(ImageData oldData, ImageData newData) throws InvalidImageDataException, PersistenceMechanismException { 
380  
381     boolean success = false; 
382     RecordStore infoStore = null; 
383     try { 
384  
385       // Parse the Data store name to get the Info store name 
386       String infoStoreName = oldData.getParentAlbumName(); 
387       infoStoreName = ImageAccessor.INFO_LABEL + infoStoreName.substring(ImageAccessor.ALBUM_LABEL.length()); 
388       infoStore = RecordStore.openRecordStore(infoStoreName, false); 
389  
390       ImageUtil converter = new ImageUtil(); 
391       byte[] imageDataBytes = converter.getBytesFromImageInfo(newData); 
392  
393       infoStore.setRecord(oldData.getRecordId(), imageDataBytes, 0, imageDataBytes.length); 
394  
395     } catch (RecordStoreException rse) { 
396       throw new PersistenceMechanismException(rse); 
397     } 
398  
399     // Update the Hashtable 'cache' 
400     setImageInfo(oldData.getImageLabel(), newData); 
401  
402     try { 
403       infoStore.closeRecordStore(); 
404     } catch (RecordStoreNotOpenException e) { 
405       //No problem if the RecordStore is not Open 
406     } catch (RecordStoreException e) { 
407       throw new PersistenceMechanismException(e); 
408     } 
409  
410     return success; 
411   } 
412  
413   /** 
414    * Retrieve the metadata associated with a specified image (by name) 
415    * @throws ImageNotFoundException  
416    * @throws NullAlbumDataReference  
417    */ 
418   public ImageData getImageInfo(String imageName) throws ImageNotFoundException, NullAlbumDataReference { 
419  
420     if (model == null) 
421       throw new NullAlbumDataReference("Null reference to the Album data"); 
422  
423     ImageData ii = (ImageData) model.getImageInfoTable().get(imageName); 
424  
425     if (ii == null) 
426       throw new ImageNotFoundException(imageName +" was NULL in ImageAccessor Hashtable."); 
427        
428  
429     return ii; 
430  
431   } 
432  
433   /** 
434    * Update the hashtable with new ImageInfo data 
435    */ 
436   public void setImageInfo(String imageName, ImageData newData) { 
437  
438     model.getImageInfoTable().put(newData.getImageLabel(), newData); 
439  
440   } 
441  
442   /** 
443    * Fetch a single image from the Record Store This should be used for 
444    * loading images on-demand (only when they are viewed or sent via SMS etc.) 
445    * to reduce startup time by loading them all at once. 
446    * @throws PersistenceMechanismException  
447    */ 
448   public Image loadSingleImageFromRMS(String recordName, String imageName, 
449       int recordId) throws PersistenceMechanismException { 
450  
451     Image img = null; 
452     byte[] imageData = loadImageBytesFromRMS(recordName, imageName, 
453         recordId); 
454     img = Image.createImage(imageData, 0, imageData.length); 
455     return img; 
456   } 
457  
458   /** 
459    * Get the data for an Image as a byte array. This is useful for sending 
460    * images via SMS or HTTP 
461    * @throws PersistenceMechanismException  
462    */ 
463   public byte[] loadImageBytesFromRMS(String recordName, String imageName, 
464       int recordId) throws PersistenceMechanismException { 
465  
466     byte[] imageData = null; 
467  
468     try { 
469  
470       RecordStore albumStore = RecordStore.openRecordStore(recordName, 
471           false); 
472       imageData = albumStore.getRecord(recordId); 
473       albumStore.closeRecordStore(); 
474  
475     } catch (RecordStoreException rse) { 
476       throw new PersistenceMechanismException(rse); 
477     } 
478  
479     return imageData; 
480   } 
481  
482   /** 
483    * Delete a single (specified) image from the (specified) record store. This 
484    * will permanently delete the image data and metadata from the device. 
485    * @throws PersistenceMechanismException  
486    * @throws NullAlbumDataReference  
487    * @throws ImageNotFoundException  
488    */ 
489   public boolean deleteSingleImageFromRMS(String storeName, String imageName) throws PersistenceMechanismException, ImageNotFoundException, NullAlbumDataReference { 
490  
491     boolean success = false; 
492  
493     // Open the record stores containing the byte data and the meta data 
494     // (info) 
495     try { 
496  
497       // Verify storeName is name without pre-fix 
498       imageRS = RecordStore 
499           .openRecordStore(ALBUM_LABEL + storeName, true); 
500       imageInfoRS = RecordStore.openRecordStore(INFO_LABEL + storeName, 
501           true); 
502  
503       ImageData imageData = getImageInfo(imageName); 
504       int rid = imageData.getForeignRecordId(); 
505  
506       imageRS.deleteRecord(rid); 
507       imageInfoRS.deleteRecord(rid); 
508  
509       imageRS.closeRecordStore(); 
510       imageInfoRS.closeRecordStore(); 
511  
512     } catch (RecordStoreException rse) { 
513       throw new PersistenceMechanismException(rse); 
514     } 
515  
516     // TODO: It's not clear from the API whether the record store needs to 
517     // be closed or not... 
518  
519     return success; 
520   } 
521  
522   /** 
523    * Define a new photo album for mobile photo users. This creates a new 
524    * record store to store photos for the album. 
525    * @throws PersistenceMechanismException  
526    * @throws InvalidPhotoAlbumNameException  
527    */ 
528   public void createNewPhotoAlbum(String albumName) throws PersistenceMechanismException, InvalidPhotoAlbumNameException { 
529      
530     RecordStore newAlbumRS = null; 
531     RecordStore newAlbumInfoRS = null; 
532     if (albumName.equals("")){ 
533       throw new InvalidPhotoAlbumNameException(); 
534     } 
535     String[] names  = getAlbumNames(); 
536     for (int i = 0; i < names.length; i++) { 
537       if (names[i].equals(albumName)) 
538         throw new InvalidPhotoAlbumNameException(); 
539     } 
540      
541     try { 
542       newAlbumRS = RecordStore.openRecordStore(ALBUM_LABEL + albumName, 
543           true); 
544       newAlbumInfoRS = RecordStore.openRecordStore( 
545           INFO_LABEL + albumName, true); 
546       newAlbumRS.closeRecordStore(); 
547       newAlbumInfoRS.closeRecordStore(); 
548     } catch (RecordStoreException rse) { 
549       throw new PersistenceMechanismException(rse); 
550     } 
551  
552   } 
553  
554   public void deletePhotoAlbum(String albumName) throws PersistenceMechanismException { 
555  
556     try { 
557       RecordStore.deleteRecordStore(ALBUM_LABEL + albumName); 
558       RecordStore.deleteRecordStore(INFO_LABEL + albumName); 
559     } catch (RecordStoreException rse) { 
560       throw new PersistenceMechanismException(rse); 
561     } 
562  
563   } 
564  
565   /** 
566    * Get the list of photo album names currently loaded. 
567    *  
568    * @return Returns the albumNames. 
569    */ 
570   public String[] getAlbumNames() { 
571     return albumNames; 
572   } 
573 }    
    |