org.projectforge.web.wicket.WicketUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.projectforge.web.wicket.WicketUtils.java

Source

/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
//         www.projectforge.org
//
// Copyright (C) 2001-2013 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition 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 General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////

package org.projectforge.web.wicket;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.Calendar;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Page;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.extensions.ajax.markup.html.AjaxEditableLabel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.image.ContextImage;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.protocol.http.RequestUtils;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Response;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.UrlUtils;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.string.StringValue;
import org.projectforge.calendar.DayHolder;
import org.projectforge.calendar.TimePeriod;
import org.projectforge.common.BeanHelper;
import org.projectforge.common.ClassHelper;
import org.projectforge.common.DateFormatType;
import org.projectforge.common.DateFormats;
import org.projectforge.common.DateHelper;
import org.projectforge.common.DateHolder;
import org.projectforge.common.NumberHelper;
import org.projectforge.common.StringHelper;
import org.projectforge.core.BaseDao;
import org.projectforge.core.ConfigXml;
import org.projectforge.web.HtmlHelper;
import org.projectforge.web.LoginPage;
import org.projectforge.web.URLHelper;
import org.projectforge.web.WebConfig;
import org.projectforge.web.calendar.DateTimeFormatter;
import org.projectforge.web.fibu.ISelectCallerPage;
import org.projectforge.web.mobile.AbstractSecuredMobilePage;
import org.projectforge.web.mobile.MenuMobilePage;
import org.projectforge.web.wicket.components.DatePanel;
import org.projectforge.web.wicket.components.LabelValueChoiceRenderer;
import org.projectforge.web.wicket.flowlayout.ComponentSize;
import org.projectforge.web.wicket.flowlayout.ComponentWrapperPanel;
import org.projectforge.web.wicket.flowlayout.FieldsetPanel;
import org.projectforge.web.wicket.flowlayout.IconPanel;
import org.projectforge.web.wicket.flowlayout.IconType;

public class WicketUtils {
    private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(WicketUtils.class);

    private static String APPLICATION_CONTEXT = "/ProjectForge";

    private static String absoluteContextPath;

    public static final String WICKET_APPLICATION_PATH = "wa/";

    public static String getContextPath() {
        return APPLICATION_CONTEXT;
    }

    static void setContextPath(final String contextPath) {
        APPLICATION_CONTEXT = contextPath;
        absoluteContextPath = null;
    }

    /**
     * Examples: https://www.projectforge.org/demo or https://www.acme.com/ProjectForge.
     * @return Absolute context path of the web application.
     */
    public static String getAbsoluteContextPath() {
        if (absoluteContextPath == null) {
            final RequestCycle requestCycle = RequestCycle.get();
            final String url = requestCycle.getUrlRenderer()
                    .renderFullUrl(Url.parse(requestCycle.urlFor(LoginPage.class, null).toString()));
            final String basePath = "/" + WICKET_APPLICATION_PATH;
            final int pos = url.indexOf(basePath);
            if (pos < 0) {
                log.warn("Couln't get base url of '" + url + "'. Sub string '" + basePath + "' expected.");
                return url;
            }
            absoluteContextPath = url.substring(0, pos);
        }
        return absoluteContextPath;
    }

    public static HttpServletRequest getHttpServletRequest(final Request request) {
        return (HttpServletRequest) request.getContainerRequest();
    }

    public static HttpServletResponse getHttpServletResponse(final Response response) {
        return (HttpServletResponse) response.getContainerResponse();
    }

    public static boolean contains(final PageParameters parameters, final String name) {
        final StringValue sval = parameters.get(name);
        if (sval == null) {
            return false;
        } else {
            return sval.isNull() == false;
        }
    }

    public static String getAsString(final PageParameters parameters, final String name) {
        final StringValue sval = parameters.get(name);
        if (sval == null || sval.isNull() == true) {
            return null;
        } else {
            return sval.toString();
        }
    }

    public static Integer getAsInteger(final PageParameters parameters, final String name) {
        final StringValue sval = parameters.get(name);
        if (sval == null || sval.isNull() == true) {
            return null;
        } else {
            return sval.toInteger();
        }
    }

    public static int getAsInt(final PageParameters parameters, final String name, final int defaultValue) {
        final StringValue sval = parameters.get(name);
        if (sval == null || sval.isNull() == true) {
            return defaultValue;
        } else {
            return sval.toInt();
        }
    }

    public static Long getAsLong(final PageParameters parameters, final String name) {
        final StringValue sval = parameters.get(name);
        if (sval == null || sval.isNull() == true) {
            return null;
        } else {
            return sval.toLong();
        }
    }

    public static Boolean getAsBooleanObject(final PageParameters parameters, final String name) {
        final StringValue sval = parameters.get(name);
        if (sval == null || sval.isNull() == true) {
            return null;
        } else {
            return sval.toBooleanObject();
        }
    }

    public static boolean getAsBoolean(final PageParameters parameters, final String name) {
        final StringValue sval = parameters.get(name);
        if (sval == null || sval.isNull() == true) {
            return false;
        } else {
            return sval.toBoolean();
        }
    }

    public static Object getAsObject(final PageParameters parameters, final String name, final Class<?> type) {
        final StringValue sval = parameters.get(name);
        if (sval == null || sval.isNull() == true) {
            return null;
        } else {
            return sval.to(type);
        }
    }

    /**
     * Renders &lt;link type="image/x-icon" rel="shortcut icon" href="favicon.ico" /&gt;
     * @param favicon The favicon file, e. g. "/ProjectForge/favicon.ico".
     */
    public static String getCssForFavicon(final String favicon) {
        return "<link type=\"image/x-icon\" rel=\"shortcut icon\" href=\"" + favicon + "\" />";
    }

    /**
     * Prepends APPLICATION_CONTEXT if url starts with '/', otherwise url is returned unchanged.
     * @param url
     */
    public static final String getAbsoluteUrl(final String url) {
        if (url.startsWith("/") == true) {
            return APPLICATION_CONTEXT + url;
        }
        return url;
    }

    /**
     * Get the url for the given path (without image path). Later, the path of the images is changeable.
     * @param requestCycle Needed to encode url.
     * @param subpath
     * @return
     */
    public static String getImageUrl(final RequestCycle requestCycle, final String path) {
        return getUrl(requestCycle, path, true);
    }

    /**
     * Should be c:url equivalent, but isn't yet (works for now).
     * @param requestCycle Needed to encode url.
     * @param path
     * @param encodeUrl
     * @return path itself if not starts with '/' otherwise "/ProjectForge" + path with session id and params.
     */
    public static String getUrl(final RequestCycle requestCycle, final String path, final boolean encodeUrl) {
        String url = UrlUtils.rewriteToContextRelative(path, requestCycle);
        if (encodeUrl == true) {
            url = requestCycle.getResponse().encodeURL(url);
        }
        return url;
    }

    /**
     * Works for Wicket and non Wicket calling pages. For non Wicket callers the pageClass must be bookmarked in Wicket application.
     * @param pageClass
     * @param Optional list of params in tupel form: key, value, key, value...
     */
    public static String getBookmarkablePageUrl(final Class<? extends Page> pageClass, final String... params) {
        final RequestCycle requestCylce = RequestCycle.get();
        if (requestCylce != null) {
            final PageParameters pageParameter = getPageParameters(params);
            return requestCylce.urlFor(pageClass, pageParameter).toString();
        } else {
            // RequestCycle.get().urlFor(pageClass, pageParameter).toString() can't be used for non wicket requests!
            final String alias = WicketApplication.getBookmarkableMountPath(pageClass);
            if (alias == null) {
                log.error("Given page class is not mounted. Please mount class in WicketApplication: " + pageClass);
                return getDefaultPageUrl();
            }
            if (params == null) {
                return WICKET_APPLICATION_PATH + alias;
            }
            final StringBuffer buf = new StringBuffer();
            buf.append(WICKET_APPLICATION_PATH).append(alias);
            try {
                for (int i = 0; i < params.length; i += 2) {
                    if (i == 0) {
                        buf.append("?");
                    } else {
                        buf.append("&");
                    }
                    buf.append(URLEncoder.encode(params[i], "UTF-8")).append("=");
                    if (i + 1 < params.length) {
                        buf.append(URLEncoder.encode(params[i + 1], "UTF-8"));
                    }
                }
            } catch (final UnsupportedEncodingException ex) {
                log.error(ex.getMessage(), ex);
            }
            return buf.toString();
        }
    }

    /**
     * Tuples of parameters converted to Wicket parameters.
     * @param params
     * @return
     */
    public static PageParameters getPageParameters(final String[] params) {
        final PageParameters pageParameters = new PageParameters();
        if (params != null) {
            for (int i = 0; i < params.length; i += 2) {
                if (i + 1 < params.length) {
                    pageParameters.add(params[i], params[i + 1]);
                } else {
                    pageParameters.add(params[i], null);
                }
            }
        }
        return pageParameters;
    }

    /**
     * @param relativePagePath
     * @return
     * @see RequestUtils#toAbsolutePath(String, String)
     * @see URLHelper#removeJSessionId(String)
     */
    public final static String toAbsolutePath(final String requestUrl, final String relativePagePath) {
        final String absoluteUrl = RequestUtils.toAbsolutePath(requestUrl, relativePagePath);
        return URLHelper.removeJSessionId(absoluteUrl);
    }

    /**
     * @param id
     * @return new PageParameters containing the given id as page parameter.
     */
    public final static PageParameters getEditPageParameters(final Integer id) {
        return new PageParameters().set(AbstractEditPage.PARAMETER_KEY_ID, id);
    }

    /**
     * @return Default page of ProjectForge. Currently {@link WicketApplication#DEFAULT_PAGE} is the default page (e. g. to redirect after
     *         login if no forward url is specified).
     */
    public static String getDefaultPageUrl() {
        return getBookmarkablePageUrl(getDefaultPage());
    }

    /**
     * @return Default page of ProjectForge. Currently {@link WicketApplication#DEFAULT_PAGE} is the default page (e. g. to redirect after
     *         cancel if no other return page is specified).
     */
    public static Class<? extends WebPage> getDefaultPage() {
        final WebConfig webConfig = ConfigXml.getInstance().getWebConfig();
        return webConfig != null ? webConfig.getDefaultPage() : WicketApplication.DEFAULT_PAGE;
    }

    /**
     * @return MenuMobilePage.class.
     */
    public static Class<? extends AbstractSecuredMobilePage> getDefaultMobilePage() {
        return MenuMobilePage.class;
    }

    /**
     * If value is null or value is default value then nothing is done. Otherwise the given value is added as page parameter under the given
     * key. Dates and TimePeriods are converted and can be gotten by {@link #getPageParameter(PageParameters, String, Class)}.
     * @param pageParameters
     * @param key
     * @param value
     * @see ClassHelper#isDefaultType(Class, Object)
     */
    public static void putPageParameter(final PageParameters pageParameters, final String key, final Object value) {
        if (value == null) {
            // Do not put null values to page parameters.
        } else if (ClassHelper.isDefaultType(value.getClass(), value)) {
            // Do not put default values to page parameters.
        } else if (value instanceof Date) {
            addOrReplaceParameter(pageParameters, key, ((Date) value).getTime());
        } else if (value instanceof TimePeriod) {
            addOrReplaceParameter(pageParameters, key, ((TimePeriod) value).getFromDate().getTime() + "-"
                    + ((TimePeriod) value).getToDate().getTime());
        } else {
            addOrReplaceParameter(pageParameters, key, value);
        }
    }

    public static void addOrReplaceParameter(final PageParameters pageParameters, final String key,
            final Object value) {
        if (pageParameters.get(key).isNull() == true) {
            pageParameters.add(key, value);
        } else {
            pageParameters.set(key, value);
        }
    }

    public static void putPageParameters(final ISelectCallerPage callerPage, final Object dataObject,
            final Object filterObject, final PageParameters pageParameters, final String[] bookmarkableProperties) {
        if (bookmarkableProperties == null) {
            return;
        }
        // final String pre = prefix != null ? prefix + "." : "";
        for (final String propertyString : bookmarkableProperties) {
            final InitialPageParameterHolder paramHolder = new InitialPageParameterHolder(propertyString);
            final Object bean;
            if (paramHolder.isFilterParameter() == true) {
                bean = filterObject;
            } else {
                bean = dataObject;
            }
            try {
                final Object value = BeanHelper.getProperty(bean, paramHolder.property);
                WicketUtils.putPageParameter(pageParameters, paramHolder.prefix + paramHolder.alias, value);
            } catch (final Exception ex) {
                log.warn("Couldn't put page parameter '" + paramHolder.property + "' of bean '" + bean
                        + "'. Ignoring this parameter.");
            }
        }
    }

    /**
     * @param pageParameters
     * @param key
     * @param objectType
     * @see #putPageParameter(PageParameters, String, Object)
     */
    public static Object getPageParameter(final PageParameters pageParameters, final String key,
            final Class<?> objectType) {
        if (objectType.isAssignableFrom(Date.class) == true) {
            final StringValue sval = pageParameters.get(key);
            if (sval.isNull() == true) {
                return null;
            }
            return new Date(sval.toLongObject());
        } else if (objectType.isAssignableFrom(Boolean.class) == true) {
            return pageParameters.get(key).toBooleanObject();
        } else if (objectType.isPrimitive() == true) {
            if (Boolean.TYPE.equals(objectType)) {
                return pageParameters.get(key).toBooleanObject();
            } else if (Integer.TYPE.equals(objectType) == true) {
                return pageParameters.get(key).toInteger();
            } else if (Long.TYPE.equals(objectType) == true) {
                return pageParameters.get(key).toLong();
            } else if (Float.TYPE.equals(objectType) == true) {
                return new Float(pageParameters.get(key).toDouble());
            } else if (Double.TYPE.equals(objectType) == true) {
                return pageParameters.get(key).toDouble();
            } else if (Character.TYPE.equals(objectType) == true) {
                return pageParameters.get(key).toChar();
            } else {
                log.warn("Primitive objectType '" + objectType + "' not yet implemented. Parameter type '" + key
                        + "' is ignored.");
            }
        } else if (Enum.class.isAssignableFrom(objectType) == true) {
            final StringValue sval = pageParameters.get(key);
            if (sval.isNull() == true) {
                return null;
            }
            final String sValue = sval.toString();
            @SuppressWarnings({ "unchecked", "rawtypes" })
            final Enum<?> en = Enum.valueOf((Class<Enum>) objectType, sValue);
            return en;
        } else if (objectType.isAssignableFrom(Integer.class) == true) {
            final StringValue sval = pageParameters.get(key);
            if (sval.isNull() == true) {
                return null;
            }
            return sval.toInteger();
        } else if (objectType.isAssignableFrom(String.class) == true) {
            return pageParameters.get(key).toString();
        } else if (objectType.isAssignableFrom(TimePeriod.class) == true) {
            final String sValue = pageParameters.get(key).toString();
            if (sValue == null) {
                return null;
            }
            final int pos = sValue.indexOf('-');
            if (pos < 0) {
                log.warn("PageParameter of type TimePeriod '" + objectType.getName() + "' in wrong format: "
                        + sValue);
                return null;
            }
            final Long fromTime = NumberHelper.parseLong(sValue.substring(0, pos));
            final Long toTime = pos < sValue.length() - 1 ? NumberHelper.parseLong(sValue.substring(pos + 1))
                    : null;
            return new TimePeriod(fromTime != null ? new Date(fromTime) : null,
                    toTime != null ? new Date(toTime) : null);
        } else {
            log.error("PageParameter of type '" + objectType.getName() + "' not yet supported.");
        }
        return null;
    }

    public static boolean hasParameter(final PageParameters parameters, final String name) {
        final StringValue sval = parameters.get(name);
        return sval != null && sval.isNull() == false;
    }

    /**
     * At least one parameter should be given for setting the fill the bean with all book-markable properties (absent properties will be set
     * to zero). If the given bean is an instance of {@link ISelectCallerPage} then the select/unselect methods are used, otherwise the
     * properties will set directly of the given bean.
     * @param bean
     * @param parameters
     * @param prefix
     * @param bookmarkableProperties
     */
    public static void evaluatePageParameters(final ISelectCallerPage callerPage, final Object dataObject,
            final Object filter, final PageParameters parameters, final String[] bookmarkableProperties) {
        if (bookmarkableProperties == null) {
            return;
        }
        // First check if any parameter is given:
        boolean useParameters = false;
        for (final String str : bookmarkableProperties) {
            final InitialPageParameterHolder paramHolder = new InitialPageParameterHolder(str);
            if (hasParameter(parameters, paramHolder.prefix + paramHolder.property) == true
                    || hasParameter(parameters, paramHolder.prefix + paramHolder.alias) == true) {
                useParameters = true;
                break;
            }
        }
        if (useParameters == false) {
            // No book-markable parameters found.
            return;
        }
        for (final String str : bookmarkableProperties) {
            final InitialPageParameterHolder paramHolder = new InitialPageParameterHolder(str);
            String key = null;
            if (hasParameter(parameters, paramHolder.prefix + paramHolder.property) == true) {
                key = paramHolder.property;
            } else if (hasParameter(parameters, paramHolder.prefix + paramHolder.alias) == true) {
                key = paramHolder.alias;
            }
            if (paramHolder.isCallerPageParameter() == true) {
                if (callerPage == null) {
                    log.warn("PageParameter '" + str + "' ignored, ISelectCallerPage isn't given.");
                } else if (key == null) {
                    callerPage.unselect(paramHolder.property);
                } else {
                    callerPage.select(paramHolder.property, parameters.get(paramHolder.prefix + key).toString());
                }
            } else {
                try {
                    final Object bean;
                    if (paramHolder.isFilterParameter() == true) {
                        // Use filter object
                        bean = filter;
                        if (bean == null) {
                            log.warn("PageParameter '" + str + "' ignored, filter isn't given.");
                            continue;
                        }
                    } else {
                        bean = dataObject;
                        if (bean == null) {
                            log.warn("PageParameter '" + str + "' ignored, dataObject isn't given.");
                            continue;
                        }
                    }
                    final Method method = BeanHelper.determineGetter(bean.getClass(), paramHolder.property);
                    if (key == null) {
                        BeanHelper.setProperty(bean, paramHolder.property,
                                ClassHelper.getDefaultType(method.getReturnType()));
                    } else {
                        final Object value = WicketUtils.getPageParameter(parameters, paramHolder.prefix + key,
                                method.getReturnType());
                        BeanHelper.setProperty(bean, paramHolder.property, value);
                    }
                } catch (final Exception ex) {
                    log.warn("Property '" + key + "' not found. Ignoring URL parameter.");
                }
            }
        }
    }

    /**
     * Adds onclick attribute with "javascript:rowClick(this);".
     * @param row Html tr element.
     */
    public static void addRowClick(final Component row) {
        row.add(AttributeModifier.replace("onclick", "javascript:rowClick(this);"));
        // add marker css class for contextMenu javaScript
        row.add(new AttributeAppender("class", Model.of("withContextMenu"), " "));
    }

    /**
     * 
     * @return
     */
    public static ContextImage getInvisibleDummyImage(final String id, final RequestCycle requestCylce) {
        final ContextImage image = new ContextImage(id,
                WicketUtils.getImageUrl(requestCylce, WebConstants.IMAGE_SPACER));
        image.setVisible(false);
        return image;
    }

    public static Component getInvisibleComponent(final String id) {
        return new Label(id).setVisible(false);
    }

    /**
     * @param label
     * @param unit
     * @return label [<unit>] (label with appended unit in brackets).
     */
    public static String getLabelWithUnit(final String label, final String unit) {
        return label + " [" + unit + "]";
    }

    /**
     * Uses "jiraSupportTooltipImage" as component id.
     * @param parent only needed for localization
     * @param id
     * @return IconPanel which is invisible if JIRA isn't configured.
     */
    public static IconPanel getJIRASupportTooltipIcon(final Component parent, final String id) {
        final IconPanel icon = new IconPanel(id, IconType.JIRA_SUPPORT,
                Model.of(parent.getString("tooltip.jiraSupport.field.title")),
                Model.of(parent.getString("tooltip.jiraSupport.field.content")));
        if (isJIRAConfigured() == false) {
            icon.setVisible(false);
        }
        return icon;
    }

    /**
     * Uses "jiraSupportTooltipImage" as component id. Please use {@link FieldsetPanel#addJIRASupportHelpIcon()} instead of this method if
     * possible.
     * @param fieldset needed for localization and for getting new child id.
     * @return IconPanel which is invisible if JIRA isn't configured.
     */
    public static IconPanel getJIRASupportTooltipIcon(final FieldsetPanel fieldset) {
        return getJIRASupportTooltipIcon(fieldset, fieldset.newIconChildId());
    }

    /**
     */
    public static IconPanel getAlertTooltipIcon(final FieldsetPanel fieldset, final String tooltip) {
        return getAlertTooltipIcon(fieldset, null, Model.of(tooltip));
    }

    /**
     */
    public static IconPanel getAlertTooltipIcon(final FieldsetPanel fieldset, final IModel<String> title,
            final IModel<String> tooltip) {
        final IconPanel icon = new IconPanel(fieldset.newIconChildId(), IconType.ALERT, title, tooltip);
        return icon;
    }

    public static final boolean isJIRAConfigured() {
        return ConfigXml.getInstance().isJIRAConfigured();
    }

    /**
     * Add JavaScript function showDeleteEntryQuestionDialog(). Depending on BaseDao.isHistorizable() a delete or mark-as-deleted question
     * will be displayed. Usage in markup: &lt;script wicket:id="showDeleteEntryQuestionDialog"&gt;[...]&lt;/script&gt;
     * @param parent
     * @param dao
     */
    public static void addShowDeleteRowQuestionDialog(final MarkupContainer parent, final BaseDao<?> dao) {
        final StringBuffer buf = new StringBuffer();
        buf.append("function showDeleteEntryQuestionDialog() {\n").append("  return window.confirm('");
        if (dao.isHistorizable() == true) {
            buf.append(parent.getString("question.markAsDeletedQuestion"));
        } else {
            buf.append(parent.getString("question.deleteQuestion"));
        }
        buf.append("');\n}\n");
        parent.add(new Label("showDeleteEntryQuestionDialog", buf.toString()).setEscapeModelStrings(false)
                .add(AttributeModifier.replace("type", "text/javascript")));
    }

    public static void setSize(Component component, final ComponentSize size) {
        if (component instanceof ComponentWrapperPanel) {
            component = ((ComponentWrapperPanel) component).getFormComponent();
        }
        component.add(AttributeModifier.append("class", size.getClassAttrValue()));
    }

    /**
     * Sets the html attribute placeholder.
     * @param component
     * @param value
     */
    public static void setPlaceHolderAttribute(Component component, final String value) {
        if (component instanceof ComponentWrapperPanel) {
            component = ((ComponentWrapperPanel) component).getFormComponent();
        }
        component.add(AttributeModifier.replace("placeholder", value));
    }

    /**
     * @param parent Only for i18n needed.
     * @param startTime Start time or null.
     * @param stopTime Stop time or null.
     * @return The weeks of year range for the given start an stop time.
     */
    public static String getCalendarWeeks(final MarkupContainer parent, final Date startTime, final Date stopTime) {
        int fromWeek = -1;
        int toWeek = -1;
        if (startTime != null) {
            fromWeek = DateHelper.getWeekOfYear(startTime);
        }
        if (stopTime != null) {
            toWeek = DateHelper.getWeekOfYear(stopTime);
        }
        if (fromWeek < 0 && toWeek < 0) {
            return "";
        }
        final StringBuffer buf = new StringBuffer();
        buf.append(parent.getString("calendar.weekOfYearShortLabel")).append(" ");
        if (fromWeek >= 0) {
            buf.append(StringHelper.format2DigitNumber(fromWeek));
            if (toWeek == -1) {
                buf.append("-");
            } else if (toWeek != fromWeek) {
                buf.append("-").append(StringHelper.format2DigitNumber(toWeek));
            }
        } else {
            buf.append("-").append(StringHelper.format2DigitNumber(toWeek));
        }
        return buf.toString();
    }

    /**
     * @param date
     */
    public static String getUTCDate(final Date date) {
        if (date == null) {
            return "";
        }
        final DateHolder dh = new DateHolder(date);
        return DateHelper.TECHNICAL_ISO_UTC.get().format(dh.getDate());
    }

    /**
     * @param label Label as prefix
     * @param date
     * @return <label>: <date>
     */
    public static String getUTCDate(final String label, final Date date) {
        if (date == null) {
            return label + ":";
        }
        final DateHolder dh = new DateHolder(date);
        return label + ": " + DateHelper.TECHNICAL_ISO_UTC.get().format(dh.getDate());
    }

    /**
     * @param startTime Start time or null.
     * @param stopTime Stop time or null.
     */
    public static String getUTCDates(final Date startTime, final Date stopTime) {
        final StringBuffer buf = new StringBuffer();
        final DateHolder start = startTime != null ? new DateHolder(startTime) : null;
        final DateHolder stop = stopTime != null ? new DateHolder(stopTime) : null;
        if (start != null) {
            buf.append(DateHelper.TECHNICAL_ISO_UTC.get().format(start.getDate()));
            if (stop != null) {
                buf.append(" - ");
            }
        }
        if (stop != null) {
            buf.append(DateHelper.TECHNICAL_ISO_UTC.get().format(stop.getDate()));
        }
        return buf.toString();
    }

    public static LabelValueChoiceRenderer<Long> getDatumChoiceRenderer(final int lastNDays) {
        final LabelValueChoiceRenderer<Long> datumChoiceRenderer = new LabelValueChoiceRenderer<Long>();
        for (int i = 0; i > -lastNDays; i--) {
            final DayHolder day = new DayHolder();
            day.add(Calendar.DAY_OF_YEAR, i);
            datumChoiceRenderer.addValue(day.getSQLDate().getTime(), DateTimeFormatter.instance()
                    .getFormattedDate(day.getSQLDate(), DateFormats.getFormatString(DateFormatType.DATE)));
        }
        return datumChoiceRenderer;
    }

    public static void append(final Component component, final RowCssClass... rowCssClasses) {
        for (final RowCssClass rowCssClass : rowCssClasses) {
            component.add(AttributeModifier.append("class", rowCssClass.getCssClass()));
        }
    }

    /**
     * Adds a SimpleAttributeModifier("title", ...) to the given component.
     * @param component
     * @param title
     * @param text
     * @see #createTooltip(String, String)
     * @see #setStyleHasTooltip(Component)
     */
    public static Component addTooltip(final Component component, final String title, final String text) {
        return addTooltip(component, Model.of(title), Model.of(text));
    }

    /**
     * Adds a SimpleAttributeModifier("title", ...) to the given component.
     * @param component
     * @param text
     * @see #createTooltip(String, String)
     * @see #setStyleHasTooltip(Component)
     */
    public static Component addTooltip(final Component component, final String text) {
        return addTooltip(component, null, Model.of(text));
    }

    /**
     * Adds a SimpleAttributeModifier("title", ...) to the given component. Does not modify the given tool tip text!
     * @param component
     * @param text
     */
    public static Component addTooltip(final Component component, final IModel<String> text) {
        return addTooltip(component, null, text);
    }

    /**
     * Adds a SimpleAttributeModifier("title", ...) to the given component. Does not modify the given tool tip text!
     * @param component
     * @param title
     * @param text If the string contains "\n" characters then html=true and &lt;br/&gt; are used.
     */
    public static Component addTooltip(final Component component, final IModel<String> title, IModel<String> text) {
        if (text != null && text.getObject() != null && text.getObject().indexOf("\n") > 0) {
            final String newText = HtmlHelper.escapeHtml(text.getObject(), true);
            text = Model.of(newText);
            component.add(AttributeModifier.replace("data-html", true));
        }
        if (title != null && title.getObject() != null) {
            component.add(AttributeModifier.replace("rel", "mypopup"));
            component.add(AttributeModifier.replace("data-original-title", title));
            component.add(AttributeModifier.replace("data-content", text));
        } else {
            component.add(AttributeModifier.replace("rel", "mytooltip"));
            component.add(AttributeModifier.replace("title", text));
        }
        return component;
    }

    /**
     * You need to use {@link AjaxEditableLabel#getLabel()}.
     * @param label
     * @return
     */
    public static Component addEditableLabelDefaultTooltip(final Component label) {
        return addTooltip(label, label.getString("form.ajaxEditableLabel.tooltip"));
    }

    public static Component setWarningTooltip(final Component component) {
        component.add(AttributeModifier.append("class", "warning"));
        return component;
    }

    /**
     * Sets readonly="readonly" and "readOnly" as class.
     * @param component
     * @return This for chaining.
     */
    public static FormComponent<?> setReadonly(final FormComponent<?> component) {
        component.add(AttributeModifier.append("class", "readonly"));
        component.add(AttributeModifier.replace("readonly", "readonly"));
        return component;
    }

    /**
     * Sets attribute size (only for TextFields) and style="length: width"; The width value is size + 0.5 em and for drop down choices size +
     * 2em;
     * @param component
     * @param size
     * @return This for chaining.
     */
    public static FormComponent<?> setSize(final FormComponent<?> component, final int size) {
        return setSize(component, size, true);
    }

    /**
     * Sets attribute size (only for TextFields) and style="length: width"; The width value is size + 0.5 em and for drop down choices size +
     * 2em;
     * @param component
     * @param size
     * @param important If true then "!important" is appended to the width style (true is default).
     * @return This for chaining.
     */
    public static FormComponent<?> setSize(final FormComponent<?> component, final int size,
            final boolean important) {
        if (component instanceof TextField) {
            component.add(AttributeModifier.replace("size", String.valueOf(size)));
        }
        final StringBuffer buf = new StringBuffer(20);
        buf.append("width: ");
        if (component instanceof DropDownChoice) {
            buf.append(size + 2).append("em");
        } else {
            buf.append(size).append(".5em");
        }
        if (important == true) {
            buf.append(" !important;");
        }
        buf.append(";");
        component.add(AttributeModifier.append("style", buf.toString()));
        return component;
    }

    /**
     * Sets attribute size (only for TextFields) and style="width: x%";
     * @param component
     * @param size
     * @return This for chaining.
     */
    public static FormComponent<?> setPercentSize(final FormComponent<?> component, final int size) {
        component.add(AttributeModifier.append("style", "width: " + size + "%;"));
        return component;
    }

    /**
     * Sets attribute font-size: style="font-size: 1.1em;";
     * @param component
     * @param size
     * @return This for chaining.
     */
    public static Component setFontSizeLarge(final Component component) {
        component.add(AttributeModifier.append("style", "font-size: 1.5em;"));
        return component;
    }

    public static Component setStrong(final Component component) {
        component.add(AttributeModifier.append("style", "font-weight: bold;"));
        return component;
    }

    /**
     * Sets attribute style="height: <height>ex;"
     * @param component
     * @param size
     * @return This for chaining.
     */
    public static FormComponent<?> setHeight(final FormComponent<?> component, final int height) {
        component.add(AttributeModifier.append("style", "height: " + height + "ex;"));
        return component;
    }

    /**
     * Adds class="focus" to the given component. It's evaluated by the adminica_ui.js. FocusOnLoadBehaviour doesn't work because the focus is
     * set to early (before the components are visible).
     * @param component
     * @return This for chaining.
     */
    public static FormComponent<?> setFocus(final FormComponent<?> component) {
        component.add(setFocus());
        return component;
    }

    /**
     * Same as {@link #setFocus(FormComponent)}
     * @return AttributeAppender
     */
    public static Behavior setFocus() {
        return new FocusOnLoadBehavior();
    }

    /**
     * For field-sets with multiple fields this method generates a multi label, such as "label1/label2", e. g. "zip code/city".
     * @param label
     * @return
     */
    public static String createMultipleFieldsetLabel(final String... labels) {
        return StringHelper.listToString("/", labels);
    }

    /**
     * If true then a tick-mark icon is returned, otherwise an invisible label.
     * @param requestCycle
     * @param componentId
     * @param value
     * @return
     */
    public static Component createBooleanLabel(final RequestCycle requestCycle, final String componentId,
            final boolean value) {
        if (value == true) {
            return new IconPanel(componentId, IconType.ACCEPT);
        }
        return new Label(componentId, "invisible").setVisible(false);
    }

    /**
     * Searchs the attribute behavior (SimpleAttributeModifier or AttibuteApendModifier) with the given attribute name and returns it if
     * found, otherwise null.
     * @param comp
     * @param name Name of attribute.
     */
    public static AttributeModifier getAttributeModifier(final Component comp, final String name) {
        for (final Behavior behavior : comp.getBehaviors()) {
            if (behavior instanceof AttributeAppender
                    && name.equals(((AttributeAppender) behavior).getAttribute()) == true) {
                return (AttributeAppender) behavior;
            } else if (behavior instanceof AttributeModifier
                    && name.equals(((AttributeModifier) behavior).getAttribute()) == true) {
                return (AttributeModifier) behavior;
            }
        }
        return null;
    }

    /**
     * Calls {@link Component#setResponsePage(Page)}. If the responseItem is an instance of a Page then setResponse for this Page is called
     * otherwise setResponse is called via {@link Component#getPage()}.
     * @param component
     * @param responseItem Page or Component.
     */
    public static void setResponsePage(final Component component, final Component responseItem) {
        if (responseItem instanceof Page) {
            component.setResponsePage((Page) responseItem);
        } else {
            component.setResponsePage(responseItem.getPage());
        }
    }

    /**
     * Casts callerPage to Component and calls {@link #setResponsePage(Component, Component)}.
     * @param component
     * @param callerPage Must be an instance of Component (otherwise a ClassCastException is thrown).
     */
    public static void setResponsePage(final Component component, final ISelectCallerPage callerPage) {
        setResponsePage(component, (Component) callerPage);
    }

    public static AttributeModifier javaScriptConfirmDialogOnClick(final String message) {
        final String escapedText = message.replace("'", "\'");
        return AttributeModifier.replace("onclick", "javascript:return showConfirmDialog('" + escapedText + "');");
    }

    @SuppressWarnings("unchecked")
    public static void setLabel(final FormComponent<?> component, final Label label) {
        final IModel<String> labelModel = (IModel<String>) label.getDefaultModel();
        if (component instanceof DatePanel) {
            ((DatePanel) component).getDateField().setLabel(labelModel);
        } else {
            component.setLabel(labelModel);
        }
    }

    public static boolean isParent(final Component parent, final Component descendant) {
        final MarkupContainer p = descendant.getParent();
        if (p == null) {
            return false;
        } else if (p == parent) {
            return true;
        } else {
            return isParent(parent, p);
        }
    }
}