package org.jboss.seam.wiki.core.upload;
import net.sf.jmimemagic.Magic;
import org.jboss.seam.ScopeType;
import org.jboss.seam.international.StatusMessages;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.log.Log;
import org.jboss.seam.wiki.core.model.WikiUpload;
import org.jboss.seam.wiki.core.upload.handler.UploadHandler;
import static org.jboss.seam.international.StatusMessage.Severity.ERROR;
import static org.jboss.seam.international.StatusMessage.Severity.WARN;
import java.util.Map;
import java.io.Serializable;
/**
* A handy conversation-scoped component we can use to bind a typical upload form to.
* <p>
* Call the <tt>uploadNewInstance()</tt> action and then access <tt>getUpload()</tt> in the same
* conversation. Uses upload handlers to instantiate and fill the data into the right
* <tt>WikiUpload</tt> subclass.
* <p>
* Call the <tt>uploadUpdateInstance(id)</tt> action to retrieve and fill the data into
* an existing <tt>WikiUpload</tt> subclass instance.
*
* @author Christian Bauer
*/
@Name("uploader")
@Scope(ScopeType.CONVERSATION)
public class Uploader implements Serializable {
@Logger
Log log;
@In
private StatusMessages statusMessages;
@In
Map<String, UploadType> uploadTypes;
UploadHandler handler;
WikiUpload upload;
private String filename;
private String contentType;
private byte[] data;
Long parentDirectoryId;
public UploadHandler getUploadHandler() {
return handler;
}
public WikiUpload getUpload() {
return upload;
}
public Long getParentDirectoryId() {
return parentDirectoryId;
}
public void setParentDirectoryId(Long parentDirectoryId) {
this.parentDirectoryId = parentDirectoryId;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
/**
* Marshall form data into a new <tt>WikiUpload</tt> instance, pick
* the right handler automatically based on the data (or browser, if magic fails) content type.
*
* @return String outcome of action, null or if successful, the simple classname of the created entity.
*/
public String uploadNewInstance() {
log.debug("uploading new instance");
if (!validateData()) return null;
resolveContentType();
UploadType uploadType = uploadTypes.get(contentType);
if (uploadType == null) {
uploadType = uploadTypes.get(UploadTypes.GENERIC_UPLOAD_TYPE);
}
upload = uploadType.getUploadHandler().handleUpload(this);
handler = uploadType.getUploadHandler();
log.debug("uploaded: " + upload);
return upload.getClass().getSimpleName();
}
/**
* Marshall form data into existing <tt>WikiUpload</tt> instance, pick
* the right handler automatically based on (possibly new) content type.
*
* @param instance The instance to be updated
* @return String outcome of action, null or if successful, the simple classname of the updated entity.
*/
public String uploadUpdateInstance(WikiUpload instance) {
return uploadUpdateInstance(instance, false);
}
/**
* Marshall form data into existing <tt>WikiUpload</tt> instance.
* <p>
* Optionally you can enforce that the same upload handler class should be used
* as has been used for the existing <tt>WikiUpload</tt>. For example, if the
* existing entity is a <tt>WikiUploadImage</tt>, its upload handler would be
* <tt>WikiUploadImageHandler</tt>, based on the content type we stored along with
* the entity. If the newly uploaded data/content type does not produce the same
* handler class, a JSF message will be queued, <tt>getUpdate()</tt> will return null,
* and no data will be marshalled.
*
* @param instance The instance to be updated
* @param forceSameHandler Force the same handler
* @return String outcome of action, null or if successful, the simple classname of the updated entity.
*/
public String uploadUpdateInstance(WikiUpload instance, boolean forceSameHandler) {
log.debug("uploading and updating existing instance: " + instance);
this.upload = instance;
if (!validateData()) return null;
resolveContentType();
UploadType newUploadType = uploadTypes.get(contentType);
UploadHandler newupUploadHandler =
newUploadType != null
? newUploadType.getUploadHandler()
: uploadTypes.get(UploadTypes.GENERIC_UPLOAD_TYPE).getUploadHandler();
if (forceSameHandler) {
log.debug("using same upload handler as for the original, based on content type: " + instance.getContentType());
UploadHandler previousUploadHandler;
UploadType previousUploadType = uploadTypes.get(instance.getContentType());
previousUploadHandler =
previousUploadType != null
? previousUploadType.getUploadHandler()
: uploadTypes.get(UploadTypes.GENERIC_UPLOAD_TYPE).getUploadHandler();
if (!previousUploadHandler.getClass().equals(newupUploadHandler.getClass())) {
statusMessages.addFromResourceBundleOrDefault(
ERROR,
"lacewiki.msg.upload.HandlersDontMatch",
"Wrong file type uploaded, please try again with a different file."
);
upload = null;
return null;
}
}
log.debug("using upload handler to marshall data: " + newupUploadHandler.getClass());
upload = newupUploadHandler.handleUpload(this, upload);
handler = newupUploadHandler;
log.debug("uploaded: " + upload);
return upload.getClass().getSimpleName();
}
public boolean hasData() {
return data != null && data.length > 0;
}
public boolean validateData() {
if (data == null || data.length == 0) {
statusMessages.addFromResourceBundleOrDefault(
WARN,
"lacewiki.msg.upload.NoData",
"Please select a file to upload"
);
return false;
}
return true;
}
public void reset() {
filename = null;
contentType = null;
data = null;
}
// Use mime magic to find the "real" content type - but if there is an exception
// (which is expected because JMimeMagic is crap) - use the browser-supplied type.
protected void resolveContentType() {
String mimeType = null;
try {
mimeType = Magic.getMagicMatch(data).getMimeType();
} catch (Exception ex) {}
contentType = mimeType != null ? mimeType : contentType;
}
}