Java tutorial
/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.celements.photo.plugin; import java.awt.Color; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.celements.photo.container.ImageDimensions; import com.celements.photo.container.ImageLibStrings; import com.celements.photo.container.ImageStrings; import com.celements.photo.container.Metadate; import com.celements.photo.container.PhotoAlbumClass; import com.celements.photo.container.PhotoImageClass; import com.celements.photo.container.PhotoMetainfoClass; import com.celements.photo.image.GenerateThumbnail; import com.celements.photo.image.Image; import com.celements.photo.image.Thumbnail; import com.celements.photo.metadata.Metainfo; import com.celements.photo.plugin.cmd.ComputeImageCommand; import com.celements.photo.service.IImageService; import com.celements.photo.utilities.AddAttachmentToDoc; import com.celements.photo.utilities.ImportFileObject; import com.celements.photo.utilities.Unzip; import com.drew.metadata.MetadataException; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.api.Api; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.classes.BaseClass; import com.xpn.xwiki.plugin.XWikiDefaultPlugin; import com.xpn.xwiki.plugin.XWikiPluginInterface; import com.xpn.xwiki.web.Utils; public class CelementsPhotoPlugin extends XWikiDefaultPlugin { private static final Log LOGGER = LogFactory.getFactory().getInstance(CelementsPhotoPlugin.class); /** * The image formats supported by the image plugin */ public enum SupportedFormat { JPG(1, "image/jpg"), JPEG(1, "image/jpeg"), PNG(2, "image/png"), GIF(3, "image/gif"), BMP(4, "image/bmp"); /** * The mime type associated to the supported format */ private String mimeType; /** * A integer code used to generate the image cache key */ private int code; SupportedFormat(int code, String mimeType) { this.mimeType = mimeType; this.code = code; } public int getCode() { return this.code; } public String getMimeType() { return this.mimeType; } } private Image image; private Thumbnail thumbnail; private Metainfo metainfo; private ComputeImageCommand computeImgCmd; // PLUGIN .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:. public CelementsPhotoPlugin(String name, String className, XWikiContext context) { super(name, className, context); image = new Image(); thumbnail = new Thumbnail(); metainfo = new Metainfo(); init(context); } public String getName() { return "celementsphoto"; } public Api getPluginApi(XWikiPluginInterface plugin, XWikiContext context) { return new CelementsPhotoPluginAPI((CelementsPhotoPlugin) plugin, context); } public void virtualInit(XWikiContext context) { //TODO move to where it's first needed, not all are using these classes // set up the wiki for the photo plugin try { generatePhotoMetainfoClass(context); generatePhotoAlbumClass(context); generatePhotoImageClass(context); } catch (XWikiException xe) { //no problem, class can be generated later or manually LOGGER.error(xe); } } public void init(XWikiContext context) { super.init(context); } public void flushCache() { //DO NOT FLUSH IMAGE CACHE, BECAUSE IT IS ON DISK AND DOES NOT HELP TO FREE MEMORY! } public void flushImageCache() { getComputeImgCmd().flushCache(); } public XWikiAttachment downloadAttachment(XWikiAttachment attachment, XWikiContext context) { if (this.isSupportedImageFormat(attachment.getMimeType(context))) { String sheight = context.getRequest().getParameter("celheight"); String swidth = context.getRequest().getParameter("celwidth"); String copyright = context.getRequest().getParameter("copyright"); String watermark = context.getRequest().getParameter("watermark"); Color defaultBg = null; String defaultBgString = context.getRequest().getParameter("background"); return getComputeImgCmd().computeImage(attachment, context, attachment, sheight, swidth, copyright, watermark, defaultBg, defaultBgString); } return attachment; } ComputeImageCommand getComputeImgCmd() { if (computeImgCmd == null) { computeImgCmd = new ComputeImageCommand(); } return computeImgCmd; } /** * @return true if the passed mime type is supported by the plugin, false otherwise. */ public boolean isSupportedImageFormat(String mimeType) { for (SupportedFormat f : SupportedFormat.values()) { if (f.getMimeType().equals(mimeType)) { return true; } } return false; } // IMAGE .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.: /** * Returns an array of ImageStrings for all images in the specified album. * * @see com.celements.photo.plugin.container.ImageStrings * @param doc XWikiDocument of the album. * @param width Desired maximum width of the thumbnails (aspect ratio is maintained). * @param height Desired maximum height of the thumbnails (aspect ratio is maintained). * @param context XWikiContext * @return Array of ImageStrings. * @throws XWikiException * @throws IOException */ public ImageStrings[] getImageList(XWikiDocument doc, int width, int height, XWikiContext context) throws XWikiException, IOException { return image.getImageList(doc, width, height, thumbnail, context); } /** * Returns an array of ImageStrings for all images in the specified album, * excluding the link to a thumbnail. This method's primar use is to get * the image's id. * * @see com.celements.photo.plugin.container.ImageStrings * @param doc XWikiDocument of the album. * @param context XWikiContext * @return Array of ImageStrings. * @throws XWikiException * @throws IOException */ public ImageStrings[] getImageListExclThumbs(XWikiDocument doc, XWikiContext context) throws XWikiException, IOException { return image.getImageListExclThumbs(doc, context); } /** * Returns wether the specified image is marked as deleted or not. * * @param doc XWikiDocument of the album. * @param id Id of the image. * @param context XWikiContext * @return true if the image is tagged as deleted. * @throws XWikiException */ public boolean isImageDeleted(XWikiDocument doc, String id, XWikiContext context) throws XWikiException { return this.image.isDeleted(doc, id, context); } /** * Set the "deleted" tag for the image to the specified value. * * @param doc XWikiDocument of the album. * @param id Id of the image. * @param deleted true to tag the image as deleted. * @param XWikiContext * @throws XWikiException */ public void setImageDeleted(XWikiDocument doc, String id, boolean deleted, XWikiContext context) throws XWikiException { this.image.setDeleted(doc, id, deleted, context); } // THUMBNAIL .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.: /** * Returns the URL to the thumbnail of a certain image in the specified * size. If the thumbnail does not exist it is created. * * @param doc XWikiDocument of the album. * @param id Id of the image. * @param width Desired width for the thumb * @param height Desired height for the thumb * @param context XWikiContext * @return The download URL for the thumb * @throws XWikiException * @throws IOException */ public String getThumbnailUrl(XWikiDocument doc, String id, int width, int height, XWikiContext context) throws XWikiException, IOException { return thumbnail.getUrl(doc, id, width, height, context); } // METADATA .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:. /** * Returns the specified metatag. If there is no metatag with the specified * name an empty metadate is returned. * * @see com.celements.photo.plugin.container.Metadate * @param doc XWikiDocument of the album. * @param id Id of the image. * @param tag The name of the desired metatag. * @param context XWikiContext * @return The specified metatag as a Metadate object. * @throws XWikiException * @throws MetadataException * @throws IOException */ public Metadate getMetatag(XWikiDocument doc, String id, String tag, XWikiContext context) throws XWikiException, MetadataException, IOException { return metainfo.getTag(doc, id, tag, context); } /** * Returns all metatags contained in the image, excluding "Unknown tag" tags. * * @see com.celements.photo.plugin.container.Metadate * @param doc XWikiDocument of the album. * @param id Id of the image. * @param XWikiContext * @return Array of Metadate objects. * @throws XWikiException * @throws MetadataException * @throws IOException */ public Metadate[] getMetadata(XWikiDocument doc, String id, XWikiContext context) throws XWikiException, MetadataException, IOException { return metainfo.getMetadataWithCondition(doc, id, ImageLibStrings.METATAG_UNKNOWN_TAG, context); } /** * Returns all metatags contained in the image, including "Unknown tag" tags. * * @see com.celements.photo.plugin.container.Metadate * @param doc XWikiDocument of the album. * @param id Id of the image. * @param XWikiContext * @return Array of Metadate objects. * @throws XWikiException * @throws MetadataException * @throws IOException */ public Metadate[] getMetadataFull(XWikiDocument doc, String id, XWikiContext context) throws XWikiException, MetadataException, IOException { return metainfo.getMetadataWithCondition(doc, id, "", context); } // DATA MANIPULATION .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.: /** * Deletes the document with the metadata attached (only the metadata * extracted from the image is deleted, but not the data generated by * CelementsPhotoPlugin). * * @param doc XWikiDocument of the album. * @param id Id of the image. * @param context XWikiContext * @throws XWikiException * @throws IOException */ public void forceClearMetadata(XWikiDocument doc, String id, XWikiContext context) throws XWikiException, IOException { XWikiDocument metadataDoc = context.getWiki().getDocument(ImageLibStrings.getPhotoSpace(doc), doc.getName() + ImageLibStrings.DOCUMENT_SEPARATOR_IMAGE + id, context); context.getWiki().deleteDocument(metadataDoc, context); } // CLASSES .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.: /** * Generates the PhotoMetainfoClass with the fields 'name' and 'description'. * * @param context The XWikiContext used to get the xwiki and save. * @return A BaseObject of the PhotoMetainfoClass * @throws XWikiException */ public BaseClass generatePhotoMetainfoClass(XWikiContext context) throws XWikiException { return (new PhotoMetainfoClass()).getNewPhotoMetainfoClass(context); } /** * Generates the PhotoAlbumClass with fields for the data space, watermark * and copyright. * * @param context The XWikiContext used to get the xwiki and save. * @return A BaseObject of the PhotoAlbumClass * @throws XWikiException */ public BaseClass generatePhotoAlbumClass(XWikiContext context) throws XWikiException { return (new PhotoAlbumClass()).getNewPhotoAlbumClass(context); } /** * Generates the PhotoImageClass with fields for different fields to cash * information about the image. * * @param context The XWikiContext used to get the xwiki and save. * @return A BaseObject of the PhotoImageClass * @throws XWikiException */ public BaseClass generatePhotoImageClass(XWikiContext context) throws XWikiException { return (new PhotoImageClass()).getNewPhotoImageClass(context); } /** * Get a List of all attachments in the specified archive and the suggested * action when importing. * * @param importFile Zip archive to check the files for the existence in the gallery. * @param galleryDoc Gallery Document to check for the files. * @param context XWikiContext * @return List of {@link ImportFileObject} for each file. * @throws XWikiException */ public List<ImportFileObject> getAttachmentFileListWithActions(XWikiAttachment importFile, XWikiDocument galleryDoc, XWikiContext context) throws XWikiException { List<ImportFileObject> resultList = new ArrayList<ImportFileObject>(); if (importFile != null) { if (isZipFile(importFile, context)) { List<String> fileList = (new Unzip()).getZipContentList(importFile.getContent(context)); String fileSep = System.getProperty("file.separator"); for (Iterator<String> fileIterator = fileList.iterator(); fileIterator.hasNext();) { String fileName = (String) fileIterator.next(); if (!fileName.endsWith(fileSep) && !fileName.startsWith(".") && !fileName.contains(fileSep + ".")) { ImportFileObject file = new ImportFileObject(fileName, getActionForFile(fileName, galleryDoc, context)); resultList.add(file); } } } else if (isImgFile(importFile, context)) { ImportFileObject file = new ImportFileObject(importFile.getFilename(), getActionForFile(importFile.getFilename(), galleryDoc, context)); resultList.add(file); } } else { LOGGER.error("zipFile='null' - galleryDoc='" + galleryDoc.getFullName() + "'"); } return resultList; } /** * For a given filename return if, in the specified gallery, its import * should be added, overwritten or skiped. * * @param fileName Filename of the file to check. * @param galleryDoc Document of the gallery to check if the file already exists. * @return action when importing: -1 skip, 0 overwrite, 1 add */ private short getActionForFile(String fileName, XWikiDocument galleryDoc, XWikiContext context) { short action = ImportFileObject.ACTION_SKIP; if (isImgFile(fileName)) { fileName = fileName.replace(System.getProperty("file.separator"), "."); fileName = context.getWiki().clearName(fileName, false, true, context); XWikiAttachment attachment = galleryDoc.getAttachment(fileName); if (attachment == null) { action = ImportFileObject.ACTION_ADD; } else { action = ImportFileObject.ACTION_OVERWRITE; } } return action; } /** * Get a specified image file in a zip archive, extract it, change it to the * desired size and save it as an attachment to the given page. * * @param zipFile File containing the image to extract. * @param unzipFileName Filename of the image to extract. * @param attachToDoc Document to attach the extracted and resized image. * @param width Width (max - aspect ratio is maintained) to resize the image to. * @param height Height (max - aspect ratio is maintained) to resize the image to. * @param context XWikiContezt * @throws XWikiException */ public void unzipFileToAttachment(XWikiAttachment zipFile, String unzipFileName, XWikiDocument attachToDoc, int width, int height, XWikiContext context) throws XWikiException { LOGGER.info("START: zip='" + zipFile.getFilename() + "' file='" + unzipFileName + "' gallery='" + attachToDoc + "' " + "width='" + width + "' height='" + height + "'"); try { ByteArrayInputStream imgFullSize = null; if (isZipFile(zipFile, context)) { imgFullSize = new ByteArrayInputStream( (new Unzip()).getFile(zipFile.getContent(context), unzipFileName).toByteArray()); } else if (isImgFile(zipFile, context)) { imgFullSize = new ByteArrayInputStream(zipFile.getContent(context)); } //TODO is there a better way to find the mime type of the file in the in stream? // -> look at http://tika.apache.org/ or maybe in image magic? String mimeType = "png"; if ((unzipFileName.lastIndexOf('.') > -1) && (!unzipFileName.endsWith("."))) { mimeType = unzipFileName.substring(unzipFileName.lastIndexOf('.') + 1); } LOGGER.debug("unzip mimetype is " + mimeType); ByteArrayOutputStream out = new ByteArrayOutputStream(); ImageDimensions id = (new GenerateThumbnail()).createThumbnail(imgFullSize, out, width, height, null, null, mimeType, null); LOGGER.info("width='" + id.width + "' height='" + id.height + "'"); LOGGER.info("output stream size: " + out.size()); unzipFileName = unzipFileName.replace(System.getProperty("file.separator"), "."); unzipFileName = context.getWiki().clearName(unzipFileName, false, true, context); XWikiAttachment att = (new AddAttachmentToDoc()).addAtachment(attachToDoc, out.toByteArray(), unzipFileName, context); LOGGER.info("attachment='" + att.getFilename() + "', gallery='" + att.getDoc().getFullName() + "' size='" + att.getFilesize() + "'"); } catch (IOException e) { LOGGER.error(e); } LOGGER.info("END file='" + unzipFileName + "'"); } private boolean isZipFile(XWikiAttachment file, XWikiContext context) { return file.getMimeType(context).equalsIgnoreCase(ImageLibStrings.MIME_ZIP) || file.getMimeType(context).equalsIgnoreCase(ImageLibStrings.MIME_ZIP_MICROSOFT); } private boolean isImgFile(XWikiAttachment file, XWikiContext context) { return file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_BMP) || file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_GIF) || file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_JPE) || file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_JPG) || file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_JPEG) || file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_PNG); } private boolean isImgFile(String fileName) { return fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_BMP) || fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_GIF) || fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_JPE) || fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_JPG) || fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_JPEG) || fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_PNG); } private IImageService getImageService() { return Utils.getComponent(IImageService.class); } /** * @deprecated instead use getDimension from ImageService. */ @Deprecated public ImageDimensions getDimension(String imageFullName, XWikiContext context) throws XWikiException { LOGGER.warn("deprecated getDimension used!"); return getImageService().getDimension(imageFullName); } }