org.cast.cwm.data.component.MultipleFileUploadField.java Source code

Java tutorial

Introduction

Here is the source code for org.cast.cwm.data.component.MultipleFileUploadField.java

Source

/*
 * Copyright 2011-2016 CAST, Inc.
 *
 * This file is part of the CAST Wicket Modules:
 * see <http://code.google.com/p/cast-wicket-modules>.
 *
 * The CAST Wicket Modules are free software: you can redistribute and/or
 * modify them under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * The CAST Wicket Modules are distributed in the hope that they will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this software.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.cast.cwm.data.component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponentPanel;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.form.upload.MultiFileUploadField;
import org.apache.wicket.model.IModel;
import org.apache.wicket.protocol.http.IMultipartWebRequest;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.resource.PackageResourceReference;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.util.convert.ConversionException;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.upload.FileItem;

/**
 * This is a re-creation of {@link MultiFileUploadField}.  It references a similar javascript
 * file but has more flexible display options.
 * 
 * Notice that this component clears its model at the end of the request, so the uploaded files MUST
 * be processed within the request they were uploaded.
 * 
 * Uses a modified javascript implementation from
 * http://the-stickman.com/web-development/javascript/upload-multiple
 * -files-with-a-single-file-element/
 * 
 * For customizing caption text see {@link #RESOURCE_LIMITED} and {@link #RESOURCE_UNLIMITED}
 * 
 * For an example of styling using CSS see the upload example in wicket-examples
 * 
 * @author jbrookover
 * @author Igor Vaynberg (ivaynberg)
 */
public class MultipleFileUploadField extends FormComponentPanel<Collection<FileUpload>>
        implements IHeaderContributor {

    private static final long serialVersionUID = 1L;

    /**
     * Represents an unlimited max count of uploads
     */
    public static final int UNLIMITED = -1;

    /**
     * Resource key used to retrieve caption message for when a limit on the number of uploads is
     * specified. The limit is represented via ${max} variable.
     * 
     * Example: org.apache.wicket.mfu.caption.limited=Files (maximum ${max}):
     */
    public static final String RESOURCE_LIMITED = "org.apache.wicket.mfu.caption.limited";

    /**
     * Resource key used to retrieve caption message for when no limit on the number of uploads is
     * specified.
     * 
     * Example: org.apache.wicket.mfu.caption.unlimited=Files:
     */
    public static final String RESOURCE_UNLIMITED = "org.apache.wicket.mfu.caption.unlimited";

    private static final String NAME_ATTR = "name";

    private static final String MAGIC_SEPARATOR = "_mf_";

    private static final ResourceReference JS = new PackageResourceReference(MultipleFileUploadField.class,
            "MultipleFileUploadField.js");

    private final WebComponent upload;
    private final WebMarkupContainer container;

    private final int max;

    private transient String[] inputArrayCache = null;

    /**
     * Constructor
     * 
     * @param id
     */
    public MultipleFileUploadField(String id) {
        this(id, null, UNLIMITED);
    }

    /**
     * Constructor
     * 
     * @param id
     * @param max
     *            max number of files a user can upload
     */
    public MultipleFileUploadField(String id, int max) {
        this(id, null, max);
    }

    /**
     * Constructor
     * 
     * @param id
     * @param model
     */
    public MultipleFileUploadField(String id, IModel<? extends Collection<FileUpload>> model) {
        this(id, model, UNLIMITED);
    }

    /**
     * Constructor
     * 
     * @param id
     * @param model
     * @param max
     *            max number of files a user can upload
     * 
     */
    @SuppressWarnings("unchecked")
    public MultipleFileUploadField(String id, IModel<? extends Collection<FileUpload>> model, int max) {
        super(id, (IModel<Collection<FileUpload>>) model);

        this.max = max;

        upload = new WebComponent("upload");
        upload.setOutputMarkupId(true);
        add(upload);

        container = new WebMarkupContainer("container");
        container.setOutputMarkupId(true);
        add(container);

    }

    /**
     * @see org.apache.wicket.markup.html.form.FormComponentPanel#onComponentTag(org.apache.wicket.markup.ComponentTag)
     */
    @Override
    protected void onComponentTag(ComponentTag tag) {
        super.onComponentTag(tag);
        // remove the name attribute added by the FormComponent
        if (tag.getAttributes().containsKey(NAME_ATTR)) {
            tag.getAttributes().remove(NAME_ATTR);
        }
    }

    /**
     * @see org.apache.wicket.Component#onBeforeRender()
     */
    @Override
    protected void onBeforeRender() {
        super.onBeforeRender();

        // auto toggle form's multipart property
        Form<?> form = findParent(Form.class);
        if (form == null) {
            // woops
            throw new IllegalStateException("Component " + getClass().getName() + " must have a "
                    + Form.class.getName() + " component above in the hierarchy");
        }
    }

    @Override
    public boolean isMultiPart() {
        return true;
    }

    /**
     * @see org.apache.wicket.markup.html.IHeaderContributor#renderHead(org.apache.wicket.markup.html.IHeaderResponse)
     */
    @Override
    public void renderHead(IHeaderResponse response) {
        // initialize the (custom) javascript library
        response.render(JavaScriptHeaderItem.forReference(JS));

        StringBuffer js = new StringBuffer();

        // Javascript Variable reference for setting properties
        String jsVarName = upload.getMarkupId() + "_ms";

        // Create MultiSelector Object
        js.append("var " + jsVarName + " = new MultiSelector(");
        js.append("'" + getInputName() + "',");
        js.append("document.getElementById('" + container.getMarkupId() + "'),");
        js.append(max + ");");

        // Set Delete Button Properties
        js.append(jsVarName + ".setDeleteElementLabel('" + getString("org.apache.wicket.mfu.delete") + "');");
        js.append(jsVarName + ".setDeleteElementType('a');");
        js.append(jsVarName + ".setDeleteElementClass('small');");

        // Register initial upload element
        js.append(jsVarName + ".addElement(document.getElementById('" + upload.getMarkupId() + "'));");

        // Render
        response.render(OnDomReadyHeaderItem.forScript(js.toString()));
    }

    /**
     * @see org.apache.wicket.markup.html.form.FormComponent#getInputAsArray()
     */
    @Override
    public String[] getInputAsArray() {
        // fake the input array as if it contained an array of all uploaded file
        // names

        if (inputArrayCache == null) {
            // this array will aggregate all input names
            ArrayList<String> names = null;

            Request request = getRequest();
            if (request instanceof IMultipartWebRequest) {
                // retrieve the filename->FileItem map from request
                Map<String, List<FileItem>> itemNameToItem = ((IMultipartWebRequest) request).getFiles();
                Iterator<Entry<String, List<FileItem>>> it = itemNameToItem.entrySet().iterator();
                while (it.hasNext()) {
                    // iterate over the map and build the list of filenames
                    Entry<String, List<FileItem>> entry = it.next();
                    String name = entry.getKey();
                    for (FileItem item : entry.getValue()) {
                        if (!Strings.isEmpty(name) && name.startsWith(getInputName() + MAGIC_SEPARATOR)
                                && !Strings.isEmpty(item.getName())) {
                            // make sure the fileitem belongs to this component and is not empty
                            names = (names != null) ? names : new ArrayList<String>();
                            names.add(name);
                        }
                    }
                }
            }
            if (names != null) {
                inputArrayCache = names.toArray(new String[names.size()]);
            }
        }
        return inputArrayCache;

    }

    /**
     * @see org.apache.wicket.markup.html.form.FormComponent#convertValue(java.lang.String[])
     */
    @Override
    protected Collection<FileUpload> convertValue(String[] value) throws ConversionException {
        // convert the array of filenames into a collection of FileItems
        Collection<FileUpload> uploads = null;
        String[] filenames = getInputAsArray();

        if (filenames != null) {
            IMultipartWebRequest request = (IMultipartWebRequest) getRequest();
            uploads = new ArrayList<FileUpload>(filenames.length);
            for (int i = 0; i < filenames.length; i++) {
                for (FileItem item : request.getFile(filenames[i]))
                    uploads.add(new FileUpload(item));
            }
        }
        return uploads;
    }

    /**
     * @see org.apache.wicket.markup.html.form.FormComponent#updateModel()
     */
    @Override
    public void updateModel() {
        final Collection<FileUpload> collection = getModelObject();

        // figure out if there is an existing model object collection for us to
        // reuse
        if (collection == null) {
            // no existing collection, push the one we created
            setDefaultModelObject(getConvertedInput());
        } else {
            // refresh the existing collection
            collection.clear();
            if (getConvertedInput() != null) {
                collection.addAll(getConvertedInput());
            }

            // push the collection in case the model is listening to
            // setobject calls
            setDefaultModelObject(collection);
        }
    }

    /**
     * @see org.apache.wicket.Component#onDetach()
     */
    @Override
    protected void onDetach() {
        // cleanup any opened filestreams
        Collection<FileUpload> uploads = getConvertedInput();
        if (uploads != null) {
            Iterator<FileUpload> it = uploads.iterator();
            while (it.hasNext()) {
                final FileUpload upload = it.next();
                upload.closeStreams();
            }
        }

        // cleanup any caches
        inputArrayCache = null;

        // clean up the model because we don't want FileUpload objects in session
        Collection<FileUpload> modelObject = getModelObject();
        if (modelObject != null) {
            modelObject.clear();
        }

        super.onDetach();
    }
}