KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > module > builders > AbstractImages


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.module.builders;
11
12 import java.util.*;
13 import org.mmbase.cache.BlobCache;
14 import org.mmbase.util.magicfile.MagicFile;
15 import org.mmbase.util.images.*;
16 import org.mmbase.module.core.*;
17 import org.mmbase.util.logging.*;
18 import org.mmbase.util.functions.*;
19
20 /**
21  * AbstractImages holds the images and provides ways to insert, retrieve and
22  * search them.
23  *
24  * @author Michiel Meeuwissen
25  * @version $Id: AbstractImages.java,v 1.47 2006/06/28 11:05:25 michiel Exp $
26  * @since MMBase-1.6
27  */

28 public abstract class AbstractImages extends AbstractServletBuilder {
29
30     private static final Logger log = Logging.getLoggerInstance(AbstractImages.class);
31
32     public final static Parameter[] HEIGHT_PARAMETERS = Parameter.EMPTY;
33     public final static Parameter[] WIDTH_PARAMETERS = Parameter.EMPTY;
34     public final static Parameter[] DIMENSION_PARAMETERS = Parameter.EMPTY;
35
36
37     public static final String JavaDoc FIELD_ITYPE = "itype";
38     public static final String JavaDoc FIELD_FILESIZE = "filesize";
39     public static final String JavaDoc FIELD_HEIGHT = "height";
40     public static final String JavaDoc FIELD_WIDTH = "width";
41
42     protected static BlobCache handleCache = new BlobCache(300) { // a few images are in memory cache.
43
public String JavaDoc getName() { return "ImageHandles"; }
44             public String JavaDoc getDescription() { return "Handles of Images (number -> handle)"; }
45         };
46     static {
47         handleCache.putCache();
48     }
49     /**
50      * Cache with 'ckey' keys.
51      * @since MMBase-1.6.2
52      */

53     abstract protected static class CKeyCache extends org.mmbase.cache.Cache {
54         protected CKeyCache(int i) {
55             super(i);
56         }
57         /**
58          * Remove all cache entries associated with a certain images node
59          * This depends now on the fact that ckeys start with the original node-number
60          */

61
62         void remove(int originalNodeNumber) {
63             String JavaDoc prefix = "" + originalNodeNumber;
64             if (log.isDebugEnabled()) {
65                 log.debug("removing " + prefix);
66             }
67             Iterator entries = entrySet().iterator();
68             while (entries.hasNext()) {
69                 Map.Entry entry = (Map.Entry)entries.next();
70                 String JavaDoc key = (String JavaDoc)entry.getKey();
71                 if (log.isDebugEnabled()) {
72                     log.debug("checking " + key);
73                 }
74                 if (key.startsWith(prefix)) {
75                     // check is obviously to crude, e.g. if node number happens to be 4,
76
// about one in 10 cache entries will be removed which need not be removed,
77
// but well, it's only a cache, it's only bad luck...
78
// 4 would be a _very_ odd number for an Image, btw..
79
if (log.isDebugEnabled()) {
80                         log.debug("removing " + key + " " + get(key));
81                     }
82                     entries.remove();
83                 }
84
85             }
86         }
87
88         void removeCacheNumber(int icacheNumber) {
89             Iterator entries = entrySet().iterator();
90             while (entries.hasNext()) {
91                 Map.Entry entry = (Map.Entry) entries.next();
92                 Object JavaDoc value = entry.getValue();
93                 if (value instanceof ByteFieldContainer) {
94                     ByteFieldContainer bf = (ByteFieldContainer) value;
95                     if (bf.number == icacheNumber) {
96                         entries.remove();
97                     }
98                 } else if (value instanceof Integer JavaDoc) {
99                     Integer JavaDoc i = (Integer JavaDoc) value;
100                     if (i.intValue() == icacheNumber) {
101                         entries.remove();
102                     }
103                 }
104
105             }
106         }
107     }
108
109     protected BlobCache getBlobCache(String JavaDoc fieldName) {
110         if (fieldName.equals(Imaging.FIELD_HANDLE)) {
111             return handleCache;
112         } else {
113             return super.getBlobCache(fieldName);
114         }
115     }
116
117     protected String JavaDoc getAssociation() {
118         return "images";
119     }
120     protected String JavaDoc getDefaultPath() {
121         return "/img.db";
122     }
123
124     /**
125      * An image's gui-indicator is of course some <img src>, but it depends on what kind of image
126      * (cached, original) what excactly it must be.
127      */

128     abstract protected String JavaDoc getGUIIndicatorWithAlt(MMObjectNode node, String JavaDoc title, Parameters a);
129
130     /**
131      * Returns GUI Indicator for node
132      * @since MMBase-1.7
133      */

134     protected String JavaDoc getSGUIIndicatorForNode(MMObjectNode node, Parameters a) {
135         return getGUIIndicatorWithAlt(node, "*", a); /// Gui indicator of a whole node.
136
}
137
138     /**
139      * @since MMBase-1.7
140      */

141     protected String JavaDoc getSGUIIndicator(MMObjectNode node, Parameters a) {
142         String JavaDoc field = a.getString("field");
143         if (field.equals(Imaging.FIELD_HANDLE) || field.equals("")) {
144             return getSGUIIndicatorForNode(node, a);
145         }
146         // other fields can be handled by the orignal gui function...
147
return getSuperGUIIndicator(field, node);
148     }
149
150
151     protected final Set IMAGE_HANDLE_FIELDS = Collections.unmodifiableSet(new HashSet(Arrays.asList(new String JavaDoc[] {FIELD_FILESIZE, FIELD_ITYPE, FIELD_HEIGHT, FIELD_WIDTH})));
152     // javadoc inherited
153
protected Set getHandleFields() {
154         return IMAGE_HANDLE_FIELDS;
155     }
156
157     /**
158      * Determine the MIME type of this image node, based on the image format.
159      */

160     public String JavaDoc getMimeType(MMObjectNode node) {
161         String JavaDoc ext = getImageFormat(node);
162         if (log.isDebugEnabled()) {
163             log.debug("Getting mimetype for node " + node.getNumber() + " " + ext);
164         }
165         if (ext.equals("")) {
166             ext = getDefaultImageType();
167         } else if (ext.startsWith("x-")) {
168             ext = ext.substring(2);
169         }
170         return Imaging.getMimeTypeByExtension(ext);
171     }
172
173     /**
174      * Whether this builders has width and height fields
175      */

176     protected boolean storesDimension() {
177         return hasField(FIELD_WIDTH) && hasField(FIELD_HEIGHT) &&
178             ! getField(FIELD_WIDTH).isVirtual() && ! getField(FIELD_HEIGHT).isVirtual();
179     }
180     /**
181      * Whether this builders has a filesize field.
182      */

183     protected boolean storesFileSize() {
184         return hasField(FIELD_FILESIZE) && ! getField(FIELD_FILESIZE).isVirtual();
185     }
186     /**
187      * Whether this builders has a filesize field.
188      */

189     protected boolean storesImageType() {
190         return hasField(FIELD_ITYPE) && ! getField(FIELD_ITYPE).isVirtual();
191     }
192
193
194     /**
195      * @since MMBase-1.8.1
196      */

197     protected Dimension getDimensionForEmptyHandle(MMObjectNode node) {
198         log.warn("Cannot get dimension of node with no 'handle' " + node + " (stores " + storesDimension() + ")");
199         return Dimension.UNDETERMINED;
200     }
201     /**
202      * Gets the dimension of given node. Also when the fields are missing, it will result a warning then.
203      */

204     protected Dimension getDimension(MMObjectNode node) {
205         if (storesDimension()) {
206             int width = node.getIntValue(FIELD_WIDTH);
207             int height = node.getIntValue(FIELD_HEIGHT);
208             if (width >= 0 && height > 0 ) {
209                 return new Dimension(width, height);
210             }
211         }
212         Dimension dim;
213         byte[] data = node.getByteValue(Imaging.FIELD_HANDLE);
214         if (data == null || data.length == 0) {
215             dim = getDimensionForEmptyHandle(node);
216         } else {
217             ImageInformer ii = Factory.getImageInformer();
218             try {
219                 dim = ii.getDimension(data);
220                 log.debug("Found dimension " + dim);
221             } catch (Exception JavaDoc ioe) {
222                 log.error(ioe);
223                 dim = Dimension.UNDETERMINED;
224             }
225         }
226
227         if (! dim.valid()) return dim;
228
229         if (storesDimension()) {
230             int width = node.getIntValue(FIELD_WIDTH);
231             int height = node.getIntValue(FIELD_HEIGHT);
232             if (width != dim.getWidth() || height != dim.getHeight()) { // avoid recursive call on fail
233
node.setValue(FIELD_WIDTH, dim.getWidth());
234                 node.setValue(FIELD_HEIGHT, dim.getHeight());
235                 if (!node.isNew()) {
236                     node.commit();
237                 }
238             }
239         } else {
240             log.warn("Requested dimension on image object without height / width fields, this may be heavy on resources!");
241         }
242
243         return dim;
244     }
245
246
247     protected int getFileSize(MMObjectNode node) {
248         if (storesFileSize()) {
249             int size = node.getIntValue(FIELD_FILESIZE);
250             if (size >= 0) {
251                 return size;
252             }
253         }
254         byte[] data = node.getByteValue(Imaging.FIELD_HANDLE);
255         if (data == null) return -1;
256         if (storesFileSize()) {
257             node.setValue(FIELD_FILESIZE, data.length);
258             if (!node.isNew()) {
259                 node.commit();
260             }
261         } else {
262             log.warn("Requested filesize on image object without filesize fields, this may be heavy on resources!");
263         }
264         return data.length;
265     }
266
267
268     /**
269      * The default image type is used when magic could not determin the type
270      */

271     protected String JavaDoc getDefaultImageType() {
272         return "jpg";
273     }
274
275     /**
276      * Determines the image type of an object and stores the content in the itype field.
277      * @param node The object to use.
278      */

279     protected String JavaDoc getImageFormat(MMObjectNode node) {
280         String JavaDoc itype = null;
281         if (storesImageType()) {
282             itype = node.getStringValue(FIELD_ITYPE);
283             int slashPos = itype.indexOf("/");
284             if (slashPos > -1) {
285                 MagicFile magicFile = MagicFile.getInstance();
286                 String JavaDoc fixedIType = magicFile.mimeTypeToExtension(itype);
287                 if ("".equals(fixedIType)) {
288                     fixedIType = itype.substring(slashPos + 1);
289                     log.warn("Could not find extension for mimetype " + itype + ", guessing " + fixedIType);
290                 }
291                 log.warn("Found an odd itype " + itype + " on node " + node.getNumber() + " (should be " + fixedIType + "?)");
292                 itype = fixedIType;
293                 node.setValue(FIELD_ITYPE, itype);
294                 if (!node.isNew()) {
295                     node.commit();
296                 }
297             }
298         }
299
300         if ((itype == null || itype.equals("")) && // itype unset
301
! node.isNull(Imaging.FIELD_HANDLE) // handle present
302
) {
303             log.debug("Determining itype for " + node.getNumber());
304             itype = "";
305             try {
306                 MagicFile magicFile = MagicFile.getInstance();
307                 String JavaDoc mimeType = magicFile.getMimeType(node.getByteValue(Imaging.FIELD_HANDLE));
308                 if (!mimeType.equals(MagicFile.FAILED)) {
309                     // determine itype
310
if (mimeType.startsWith("image/")) {
311                         itype = mimeType.substring(6);
312                         log.debug("set itype to " + itype);
313                     } else {
314                         log.warn("Mimetype " + mimeType + " is not an image type");
315                         int pos = mimeType.indexOf('/');
316                         itype = mimeType.substring(pos + 1);
317                     }
318                 } else {
319                     log.warn(MagicFile.FAILED);
320                     itype = getDefaultImageType();
321                 }
322             } catch (Exception JavaDoc e) {
323                 log.warn("Error while determining image mimetype : " + Logging.stackTrace(e));
324             }
325             if (storesImageType()) {
326                 log.debug("Found itype " + itype + " storing that");
327                 node.setValue(FIELD_ITYPE, itype);
328                 if (!node.isNew()) {
329                     node.commit();
330                 }
331             } else {
332                 log.debug("Doesn't store");
333             }
334         }
335         return itype;
336     }
337
338     //javadoc inherited
339
protected void checkHandle(MMObjectNode node) {
340         super.checkHandle(node);
341         if (storesFileSize()) {
342             getFileSize(node);
343         }
344         if (storesDimension()) {
345             getDimension(node);
346         }
347         if (storesImageType()) {
348             getImageFormat(node);
349         }
350     }
351
352
353
354     /**
355      * Every image of course has a format and a mimetype. Two extra functions to get them.
356      *
357      */

358
359     protected Object JavaDoc executeFunction(MMObjectNode node, String JavaDoc function, List args) {
360         if (function.equals("mimetype")) {
361             return getMimeType(node);
362         } else if (function.equals("format")) {
363             return getImageFormat(node);
364         } else if ("width".equals(function)) {
365             return new Integer JavaDoc(getDimension(node).getWidth());
366         } else if ("height".equals(function)) {
367             return new Integer JavaDoc(getDimension(node).getHeight());
368         } else if ("dimension".equals(function)) {
369             return getDimension(node);
370         } else {
371             return super.executeFunction(node, function, args);
372         }
373     }
374
375 }
376
Popular Tags