001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
016 *
017 * $Id: InputFileSizeValidator.java 28460 2008-01-03 15:34:05Z sfermigier $
018 */
019
020package org.nuxeo.ecm.platform.ui.web.component.file;
021
022import javax.faces.component.StateHolder;
023import javax.faces.component.UIComponent;
024import javax.faces.context.FacesContext;
025import javax.faces.validator.Validator;
026import javax.faces.validator.ValidatorException;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.nuxeo.ecm.core.api.Blob;
031
032import com.sun.faces.util.MessageFactory;
033
034/**
035 * Input file size validator.
036 * <p>
037 * Validates an {@link InputFileInfo} blob value in case it's been uploaded. Value is set using the "maxSize" attribute
038 * and setting it to (for instance) "10Ko", "10Mo" or "10Go".
039 *
040 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
041 */
042public class InputFileSizeValidator implements Validator, StateHolder {
043
044    public static final String VALIDATOR_ID = "InputFileSizeValidator";
045
046    private static final Log log = LogFactory.getLog(InputFileSizeValidator.class);
047
048    private String maxSize = null;
049
050    private boolean maximumSet = false;
051
052    private boolean transientValue = false;
053
054    /**
055     * The message identifier of the {@link javax.faces.application.FacesMessage} to be created if the maximum size
056     * check fails. The message format string for this message may optionally include the following placeholders:
057     * <ul>
058     * <li><code>{0}</code> replaced by the configured maximum length.</li>
059     * </ul>
060     */
061    public static final String MAXIMUM_MESSAGE_ID = "error.inputFile.maxSize";
062
063    @Override
064    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
065        if (!maximumSet) {
066            return;
067        }
068        if (context == null || component == null) {
069            throw new IllegalArgumentException();
070        }
071        if (value != null) {
072            if (value instanceof InputFileInfo) {
073                InputFileInfo info = (InputFileInfo) value;
074                String choice = info.getConvertedChoice();
075                if (!(InputFileChoice.isUploadOrKeepTemp(choice))) {
076                    return;
077                }
078                Blob blob = info.getConvertedBlob();
079                long finalMaxSize = 0L;
080                String maxString = null;
081                if (maxSize != null) {
082                    finalMaxSize = getMaxSizeBytes();
083                    maxString = maxSize;
084                }
085                if (finalMaxSize != 0L && blob.getLength() > finalMaxSize) {
086                    throw new ValidatorException(MessageFactory.getMessage(context, MAXIMUM_MESSAGE_ID, maxString));
087                }
088            }
089        }
090    }
091
092    private static long parseMaxSizeString(String maxSize) {
093        long res = 0L;
094        if (maxSize != null) {
095            maxSize = maxSize.trim();
096            if (maxSize.length() < 2) {
097                log.error("Invalid maximum size " + maxSize);
098                return res;
099            }
100            Integer maxSizeInt;
101            String suffix = maxSize.substring(maxSize.length() - 2);
102            String maxSizeIntStr = maxSize.substring(0, maxSize.length() - 2).trim();
103            try {
104                maxSizeInt = Integer.valueOf(maxSizeIntStr);
105            } catch (NumberFormatException e) {
106                log.error("Invalid maximum size " + maxSize);
107                return res;
108            }
109            // Using IS units
110            if ("Ko".equals(suffix)) {
111                res = maxSizeInt * 1000;
112            } else if ("Mo".equals(suffix)) {
113                res = maxSizeInt * 1000 * 1000;
114            } else if (maxSize.endsWith("Go")) {
115                res = maxSizeInt * 1000 * 1000 * 1000;
116            } else {
117                log.error("Invalid maximum size " + maxSize);
118            }
119        }
120        return res;
121    }
122
123    public String getMaxSize() {
124        return maxSize;
125    }
126
127    public long getMaxSizeBytes() {
128        return parseMaxSizeString(maxSize);
129    }
130
131    public void setMaxSize(String maxSizeString) {
132        maxSize = maxSizeString;
133        maximumSet = true;
134    }
135
136    @Override
137    public boolean isTransient() {
138        return transientValue;
139    }
140
141    @Override
142    public void setTransient(boolean newTransientValue) {
143        transientValue = newTransientValue;
144    }
145
146    @Override
147    public Object saveState(FacesContext context) {
148        Object[] values = new Object[2];
149        values[0] = maxSize;
150        values[1] = maximumSet ? Boolean.TRUE : Boolean.FALSE;
151        return values;
152    }
153
154    @Override
155    public void restoreState(FacesContext context, Object state) {
156        Object[] values = (Object[]) state;
157        maxSize = (String) values[0];
158        maximumSet = (Boolean) values[1];
159    }
160
161}