/**
 * Provides access to the settings of an ADE container element.
 *
 * <p>
 *
 * @since 8.0
 */
public class CmsJspTagElementSetting extends TagSupport {

  /** Serial version UID required for safe serialization. */
  private static final long serialVersionUID = -7847101480288189549L;

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsJspTagElementSetting.class);

  /** The default value. */
  private String m_defaultValue;

  /** Indicates if HTML should be escaped. */
  private boolean m_escapeHtml;

  /** The name of the element setting to read. */
  private String m_elementSetting;

  /**
   * Internal action method.
   *
   * <p>
   *
   * @param req the current request
   * @return a map that contains the element settings
   */
  public static Map<String, String> elementSettingTagAction(ServletRequest req) {

    CmsFlexController controller = CmsFlexController.getController(req);

    CmsObject cms = controller.getCmsObject();
    // try to find element setting on the container element
    try {
      CmsContainerElementBean currentElement = OpenCms.getADEManager().getCurrentElement(req);
      currentElement.initResource(cms);
      return currentElement.getSettings();
    } catch (CmsException e) {
      // most likely we are not in a container page
      LOG.debug(e.getLocalizedMessage(), e);
      return Collections.emptyMap();
    }
  }

  /**
   * Internal action method.
   *
   * <p>
   *
   * @param setting the setting to look up
   * @param defaultValue the default value
   * @param escape if the result String should be HTML escaped or not
   * @param req the current request
   * @return the value of the element setting or <code>null</code> if not found
   */
  public static String elementSettingTagAction(
      String setting, String defaultValue, boolean escape, ServletRequest req) {

    String value = elementSettingTagAction(req).get(setting);
    if (value == null) {
      value = defaultValue;
    }
    if (escape) {
      // HTML escape the value
      value = CmsEncoder.escapeHtml(value);
    }
    return value;
  }

  /**
   * @return SKIP_BODY
   * @see javax.servlet.jsp.tagext.Tag#doStartTag()
   */
  @Override
  public int doStartTag() throws JspException {

    ServletRequest req = pageContext.getRequest();

    // This will always be true if the page is called through OpenCms
    if (CmsFlexController.isCmsRequest(req)) {

      try {
        String setting = elementSettingTagAction(getName(), m_defaultValue, m_escapeHtml, req);
        // Make sure that no null String is returned
        if (setting == null) {
          setting = "";
        }
        pageContext.getOut().print(setting);

      } catch (Exception ex) {
        if (LOG.isErrorEnabled()) {
          LOG.error(
              Messages.get().getBundle().key(Messages.ERR_PROCESS_TAG_1, "elementSetting"), ex);
        }
        throw new javax.servlet.jsp.JspException(ex);
      }
    }
    return SKIP_BODY;
  }

  /**
   * Returns the default value.
   *
   * <p>
   *
   * @return the default value
   */
  public String getDefault() {

    return m_defaultValue != null ? m_defaultValue : "";
  }

  /**
   * The value of the escape HTML flag.
   *
   * <p>
   *
   * @return the value of the escape HTML flag
   */
  public String getEscapeHtml() {

    return String.valueOf(m_escapeHtml);
  }

  /**
   * Returns the selected element setting name.
   *
   * <p>
   *
   * @return the selected element setting name
   */
  public String getName() {

    return m_elementSetting != null ? m_elementSetting : "";
  }

  /** @see javax.servlet.jsp.tagext.Tag#release() */
  @Override
  public void release() {

    super.release();
    m_elementSetting = null;
    m_defaultValue = null;
    m_escapeHtml = false;
  }

  /**
   * Sets the default value.
   *
   * <p>This is used if a selected element setting is not found.
   *
   * <p>
   *
   * @param def the default value
   */
  public void setDefault(String def) {

    if (def != null) {
      m_defaultValue = def;
    }
  }

  /**
   * Set the escape HTML flag.
   *
   * <p>
   *
   * @param value must be <code>"true"</code> or <code>"false"</code> (all values other then <code>
   *     "true"</code> are considered to be false)
   */
  public void setEscapeHtml(String value) {

    if (value != null) {
      m_escapeHtml = Boolean.valueOf(value.trim()).booleanValue();
    }
  }

  /**
   * Sets the element setting name.
   *
   * <p>
   *
   * @param name the element setting name to set
   */
  public void setName(String name) {

    if (name != null) {
      m_elementSetting = name;
    }
  }
}
/**
 * Dialog to edit existing submitted data in the administration view.
 *
 * <p>
 *
 * @author Anja Roettgers
 * @version $Revision: 1.8 $
 * @since 7.0.4
 */
public class CmsFormEditDialog extends CmsWidgetDialog {

  /** The length to cut the string text. */
  public static final int STRING_TRIM_SIZE = 200;

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsFormEditDialog.class);

  /** Defines which pages are valid for this dialog. */
  private static final String[] PAGES = {"page1"};

  /** Localized messages keys prefix. */
  private static final String WEBFORM_KEY_PREFIX = "webform_prefix";

  /** Constant indicating that no upload folder was defined. */
  private static final String WEBFORM_UPLOADFOLDER_NONE = "none";

  /** a map with all fields and values. */
  private HashMap<String, CmsFormDataEditBean> m_fields;

  /** contains the original data of current entry.* */
  private CmsFormDataBean m_formData;

  /** Contains the id of the current entry.* */
  private String m_paramEntryid;

  /** Contains the id of the current form.* */
  private String m_paramFormid;

  /**
   * Public constructor with JSP action element.
   *
   * <p>
   *
   * @param jsp an initialized JSP action element
   */
  public CmsFormEditDialog(CmsJspActionElement jsp) {

    super(jsp);
  }

  /**
   * Public constructor with JSP variables.
   *
   * <p>
   *
   * @param context the JSP page context
   * @param req the JSP request
   * @param res the JSP response
   */
  public CmsFormEditDialog(PageContext context, HttpServletRequest req, HttpServletResponse res) {

    this(new CmsJspActionElement(context, req, res));
  }

  /** @see org.opencms.workplace.CmsWidgetDialog#actionCommit() */
  @Override
  public void actionCommit() {

    List<Exception> errors = new ArrayList<Exception>();
    try {
      // get the list of all fields
      List<String> columnNames =
          CmsFormDataAccess.getInstance().readFormFieldNames(m_paramFormid, 0, Long.MAX_VALUE);

      // for each field look if the value has changed and update the database
      String column = null;
      CmsFormDataEditBean data;
      String value = null;
      String orgValue;
      for (int i = 0; i < columnNames.size(); i++) {
        try {
          // get for the field the old and new value
          column = columnNames.get(i);
          data = m_fields.get(column);
          orgValue = m_formData.getFieldValue(column);
          value = data.getValue();
          if (LOG.isDebugEnabled()) {
            LOG.debug(
                Messages.get()
                    .getBundle()
                    .key(
                        Messages.LOG_COMPARE_FORM_FIELDS_4,
                        new String[] {column, value, orgValue, m_paramEntryid}));
          }

          // compares the old and new value and update the database if not identical
          if (!compareValues(orgValue, value)
              || ((value != null) && (value.trim().length() == 0))) {
            CmsFormDataAccess.getInstance()
                .updateFieldValue(Integer.parseInt(m_paramEntryid), column, value);
            if (LOG.isDebugEnabled()) {
              LOG.debug(
                  Messages.get()
                      .getBundle()
                      .key(Messages.LOG_WRITE_FORM_FIELDS_3, column, value, m_paramEntryid));
            }
          }
        } catch (Exception e) {
          if (LOG.isErrorEnabled()) {
            LOG.error(
                Messages.get()
                    .getBundle()
                    .key(Messages.ERR_WRITE_FORM_FIELDS_3, column, value, m_paramEntryid));
          }
          errors.add(
              new CmsException(
                  Messages.get()
                      .container(Messages.ERR_WRITE_FORM_FIELDS_3, column, value, m_paramEntryid)));
        }
      }
    } catch (Exception ex) {
      errors.add(ex);
    }
    // set the list of errors to display when saving failed
    setCommitErrors(errors);
  }

  /**
   * Returns the paramEntryid.
   *
   * <p>
   *
   * @return the paramEntryid
   */
  public String getParamEntryid() {

    return m_paramEntryid;
  }

  /**
   * Returns the formId.
   *
   * <p>
   *
   * @return the formId
   */
  public String getParamFormid() {

    return m_paramFormid;
  }

  /** @see org.opencms.workplace.CmsWorkplace#keyDefault(java.lang.String, java.lang.String) */
  @Override
  public String keyDefault(String keyName, String defaultValue) {

    return getMessages().keyDefault(keyName, CmsStringUtil.escapeHtml(defaultValue));
  }

  /**
   * Sets the paramEntryid.
   *
   * <p>
   *
   * @param paramEntryid the paramEntryid to set
   */
  public void setParamEntryid(String paramEntryid) {

    m_paramEntryid = paramEntryid;
  }

  /**
   * Sets the formId.
   *
   * <p>
   *
   * @param formId the formId to set
   */
  public void setParamFormid(String formId) {

    if (formId == null) {
      formId = "";
    }
    m_paramFormid = formId;
  }

  /** @see org.opencms.workplace.CmsWidgetDialog#defineWidgets() */
  @Override
  protected void defineWidgets() {

    try {
      setKeyPrefix(WEBFORM_KEY_PREFIX);
      addStaticWidgets();
      addDynamicWidgets();
    } catch (Exception e) {
      if (LOG.isErrorEnabled()) {
        LOG.error(Messages.get().container(Messages.ERR_SHOW_EDIT_FORM_FIELDS_1, m_paramEntryid));
      }
    }
  }

  /** @see org.opencms.workplace.CmsWidgetDialog#getPageArray() */
  @Override
  protected String[] getPageArray() {

    return PAGES;
  }

  /** @see org.opencms.workplace.CmsWorkplace#initMessages() */
  @Override
  protected void initMessages() {

    // add specific dialog resource bundle
    addMessages(Messages.get().getBundleName());
    super.initMessages();
  }

  /** @see org.opencms.workplace.CmsWidgetDialog#validateParamaters() */
  @Override
  protected void validateParamaters() throws Exception {

    if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_paramEntryid)
        || CmsStringUtil.isEmptyOrWhitespaceOnly(m_paramFormid)) {
      throw new Exception();
    }
    m_formData = CmsFormDataAccess.getInstance().readForm(Integer.parseInt(m_paramEntryid));
  }

  /**
   * Creates the dynamic widgets for the current submitted data for each field.
   *
   * <p>
   *
   * @throws Exception if something goes wrong
   */
  private void addDynamicWidgets() throws Exception {

    if (m_fields == null) {
      m_fields = new HashMap<String, CmsFormDataEditBean>();
    }

    // get the list of all fields
    List<String> columnNames =
        CmsFormDataAccess.getInstance().readFormFieldNames(m_paramFormid, 0, Long.MAX_VALUE);

    // determine if the columns can be edited by the current user
    boolean editable;
    try {
      CmsResource formFile = getCms().readResource(m_formData.getResourceId());
      editable =
          OpenCms.getRoleManager().hasRole(getCms(), CmsRole.DATABASE_MANAGER)
              || getCms()
                  .hasPermissions(
                      formFile, CmsPermissionSet.ACCESS_WRITE, false, CmsResourceFilter.ALL);
    } catch (CmsException e) {
      // error reading form resource, only check roles of current user
      editable = OpenCms.getRoleManager().hasRole(getCms(), CmsRole.DATABASE_MANAGER);
    }

    String uploadFolder =
        OpenCms.getModuleManager()
            .getModule(CmsForm.MODULE_NAME)
            .getParameter(CmsForm.MODULE_PARAM_UPLOADFOLDER, WEBFORM_UPLOADFOLDER_NONE);

    // for each column create a widget
    String column;
    String value;
    CmsFormDataEditBean edit;
    for (int i = 0; i < columnNames.size(); i++) {

      // get the entry and fill the columns
      column = columnNames.get(i);
      value = m_formData.getFieldValue(column);
      if (CmsStringUtil.isEmpty(value)) {
        value = "";
      }
      edit = createEditEntry(value, uploadFolder, editable);
      addWidget(
          new CmsWidgetDialogParameter(
              edit, "value", column, "", PAGES[0], edit.getWidget(), 0, 1));
      m_fields.put(column, edit);
    }
  }

  /**
   * Creates the static widgets for the current submitted data.
   *
   * <p>
   *
   * @throws Exception if something goes wrong
   */
  private void addStaticWidgets() throws Exception {

    // add the id widget
    CmsFormDataEditBean edit = new CmsFormDataEditBean(m_formData.getFormId(), null);
    addWidget(
        new CmsWidgetDialogParameter(
            edit,
            "value",
            key(Messages.GUI_COLUMN_FIELDS_ID_0),
            "",
            PAGES[0],
            new CmsDisplayWidget(),
            1,
            1));

    // add the created date widget
    edit =
        new CmsFormDataEditBean(
            Messages.get().getBundle().getDateTime(m_formData.getDateCreated()), null);
    addWidget(
        new CmsWidgetDialogParameter(
            edit,
            "value",
            key(Messages.GUI_COLUMN_FIELDS_DATE_0),
            "",
            PAGES[0],
            new CmsDisplayWidget(),
            1,
            1));

    // add the resource widget
    String path;
    try {
      path = getCms().readResource(m_formData.getResourceId()).getRootPath();
    } catch (Exception e) {
      path = m_formData.getResourceId().toString();
    }
    edit = new CmsFormDataEditBean(path, null);
    addWidget(
        new CmsWidgetDialogParameter(
            edit,
            "value",
            key(Messages.GUI_COLUMN_FIELDS_RESOURCE_0),
            "",
            PAGES[0],
            new CmsDisplayWidget(),
            1,
            1));
  }

  /**
   * Compares the given values if they are identical.
   *
   * <p>
   *
   * @param value1 the first string can also be <code>null</code>
   * @param value2 the second string can also be <code>null</code>
   * @return <code>true</code>if identical otherwise <code>false</code>
   */
  private boolean compareValues(Object value1, Object value2) {

    boolean result = false;
    if ((value1 == null) && (value2 == null)) {
      return !result;
    }
    if ((value1 != null) && value1.equals(value2)) {
      return !result;
    }
    return result;
  }

  /**
   * Creates the Objects to edit the dynamic columns.
   *
   * <p>
   *
   * @param value the current value
   * @param uploadFolder the upload folder path
   * @param editable indicates if the entry can be edited by the current user
   * @return the Object contains the current value and the widget to edit
   */
  private CmsFormDataEditBean createEditEntry(String value, String uploadFolder, boolean editable) {

    I_CmsWidget widget;

    if (!uploadFolder.equals(WEBFORM_UPLOADFOLDER_NONE)
        && (value != null)
        && value.startsWith(uploadFolder)) {
      widget = new CmsFormFileWidget();
    } else if (!editable) {
      widget = new CmsDisplayWidget();
    } else if (isTextareaWidget(value)) {
      widget = new CmsTextareaWidget(5);
    } else {
      widget = new CmsInputWidget();
    }
    return new CmsFormDataEditBean(value, widget);
  }

  /**
   * Checks if the given String value is needed a textarea or a normal input field.
   *
   * <p>
   *
   * @param value the value to check if a textarea widget needed
   * @return <code>true</code>if a textarea widget is needed otherwise <code>false</code>
   */
  private boolean isTextareaWidget(String value) {

    boolean result = false;

    if (value != null) {

      String escape = CmsStringUtil.escapeHtml(value);
      boolean length = (value.length() > STRING_TRIM_SIZE);
      length &= escape.matches(".*(<.{1,5}>|[ \\t\\n\\x0B\\f\\r]).*");

      Pattern pat = Pattern.compile(".*(<.{1,5}>|[\\t\\n\\x0B\\f\\r]).*", Pattern.DOTALL);
      Matcher match = pat.matcher(escape);
      result = (length || match.matches());
    }
    return result;
  }
}
/**
 * An option of a select type widget.
 *
 * <p>If options are passed from XML content schema definitions as widget configuration options, the
 * following syntax is used for defining the option values:
 *
 * <p><code>
 * value='{text}' default='{true|false}' option='{text}' help='{text}|{more option definitions}
 * </code>
 *
 * <p>For example:
 *
 * <p><code>
 * value='value1' default='true' option='option1' help='help1'|value='value2' option='option2' help='help2'
 * </code>
 *
 * <p>The elements <code>default</code>, <code>option</code> and <code>help</code> are all optional,
 * only a <code>value</code> must be present in the input. There should be only one <code>default
 * </code> set to <code>true</code> in the input, if more than one is detected, only the first
 * <code>default</code> found is actually used. If no <code>option</code> is given, the value of
 * <code>option</code> defaults to the value of the given <code>value</code>. If no <code>help
 * </code> is given, the default is <code>null</code>.
 *
 * <p>Shortcut syntax options:
 *
 * <p>If you don't specify the <code>value</code> key, the value is assumed to start at the first
 * position of an option definition. In this case the value must not be surrounded by the <code>'
 * </code> chars. Example: <code>value='some value' default='true'</code> can also be written as
 * <code>some value default='true'</code>.
 *
 * <p>Only if you use the short value definition as described above, a default value can be marked
 * with a <code>*</code> at the end of the value definition. Example: <code>
 * value='some value' default='true'</code> can also be written as <code>some value*</code>.
 *
 * <p>Only if you use the short value definition as described above, you can also append the <code>
 * option</code> to the <code>value</code> using a <code>:</code>. In this case no <code>'</code>
 * must surround the <code>option</code>. Please keep in mind that in this case the value itself can
 * not longer contain a <code>:</code> char, since it would then be interpreted as a delimiter.
 * Example: <code>value='some value' option='some option'</code> can also be written as <code>
 * some value:some option</code>.
 *
 * <p>Any combinations of the above described shortcuts are allowed in the configuration option
 * String. Here are some more examples of valid configuration option Strings:
 *
 * <p><code>1*|2|3|4|5|6|7</code><br>
 * <code>1 default='true'|2|3|4|5|6|7</code><br>
 * <code>value='1' default='true'|value='2'|value='3'</code><br>
 * <code>value='1'|2*|value='3'</code><br>
 * <code>1*:option text|2|3|4</code><br>
 * <code>1* option='option text' help='some'|2|3|4</code>
 *
 * <p>Please note: If an entry in the configuration String is malformed, this error is silently
 * ignored (but written to the log channel of this class at <code>INFO</code>level.
 *
 * <p>
 *
 * @since 6.0.0
 */
public class CmsSelectWidgetOption {

  /** Optional shortcut default marker. */
  private static final char DEFAULT_MARKER = '*';

  /** Delimiter between option sets. */
  private static final char INPUT_DELIMITER = '|';

  /** Key prefix for the 'default'. */
  private static final String KEY_DEFAULT = "default='";

  /** Key prefix for the 'help' text. */
  private static final String KEY_HELP = "help='";

  /** Key prefix for the 'option' text. */
  private static final String KEY_OPTION = "option='";

  /** Key prefix for the 'value'. */
  private static final String KEY_VALUE = "value='";

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsSelectWidgetOption.class);

  /** Optional shortcut option delimiter. */
  private static final char OPTION_DELIMITER = ':';

  /** Indicates if this is the default value of the selection. */
  private boolean m_default;

  /** The hashcode of this object. */
  private int m_hashcode;

  /** The (optional) help text of this select option. */
  private String m_help;

  /** The (optional) display text of this select option. */
  private String m_option;

  /** The value of this select option. */
  private String m_value;

  /**
   * Creates a new select option for the given value.
   *
   * <p>
   *
   * @param value the value of this select option
   */
  public CmsSelectWidgetOption(String value) {

    this(value, false, null, null);
  }

  /**
   * Creates a new select option form the given values.
   *
   * <p>
   *
   * @param value the value of this select option
   * @param isDefault indicates if this is the default value of the selection (default is <code>
   *     false</code>)
   */
  public CmsSelectWidgetOption(String value, boolean isDefault) {

    this(value, isDefault, null, null);
  }

  /**
   * Creates a new select option form the given values.
   *
   * <p>
   *
   * @param value the value of this select option
   * @param isDefault indicates if this is the default value of the selection (default is <code>
   *     false</code>)
   * @param optionText the (optional) display text of this select option
   */
  public CmsSelectWidgetOption(String value, boolean isDefault, String optionText) {

    this(value, isDefault, optionText, null);
  }

  /**
   * Creates a new select option form the given values.
   *
   * <p>
   *
   * @param value the value of this select option
   * @param isDefault indicates if this is the default value of the selection (default is <code>
   *     false</code>)
   * @param optionText the (optional) display text of this select option
   * @param helpText the (optional) help text of this select option
   */
  public CmsSelectWidgetOption(
      String value, boolean isDefault, String optionText, String helpText) {

    m_default = isDefault;
    m_value = value;
    m_option = optionText;
    m_help = helpText;
  }

  /**
   * Returns a select widget configuration String created from the given list of select options.
   *
   * <p>If an element found in the given list is not of type <code>{@link CmsSelectWidgetOption}
   * </code>, it is ignored.
   *
   * <p>
   *
   * @param options the list of select options to create the configuration String for
   * @return a select widget configuration String created from the given list of select options
   */
  public static String createConfigurationString(List<CmsSelectWidgetOption> options) {

    if ((options == null) || (options.size() == 0)) {
      return "";
    }
    StringBuffer result = new StringBuffer(256);
    boolean first = true;
    for (int i = 0; i < options.size(); i++) {
      CmsSelectWidgetOption o = options.get(i);
      if (!first) {
        result.append(CmsSelectWidgetOption.INPUT_DELIMITER);
      } else {
        first = false;
      }
      result.append(o.toString());
    }
    return result.toString();
  }

  /**
   * Returns the default option from the given list of select options, or <code>null</code> in case
   * there is no default option in the given list.
   *
   * <p>If an element found in the given list is not of type <code>{@link CmsSelectWidgetOption}
   * </code>, this is ignored.
   *
   * <p>
   *
   * @param options the list of select options to get the default from
   * @return the default option from the given list of select options, or <code>null</code> in case
   *     there is no default option
   */
  public static CmsSelectWidgetOption getDefaultOption(List<CmsSelectWidgetOption> options) {

    if ((options == null) || (options.size() == 0)) {
      return null;
    }
    for (int i = 0; i < options.size(); i++) {
      Object o = options.get(i);
      if (o instanceof CmsSelectWidgetOption) {
        CmsSelectWidgetOption option = (CmsSelectWidgetOption) o;
        if (option.isDefault()) {
          return option;
        }
      }
    }
    return null;
  }

  /**
   * Returns a list of default options from the given list of select options.
   *
   * <p>If an element found in the given list is not of type <code>{@link CmsSelectWidgetOption}
   * </code>, this is ignored.
   *
   * <p>
   *
   * @param options the list of select options to get the default from
   * @return a list of <code>{@link CmsSelectWidgetOption}</code> objects
   */
  public static List<CmsSelectWidgetOption> getDefaultOptions(List<CmsSelectWidgetOption> options) {

    List<CmsSelectWidgetOption> defaults = new ArrayList<CmsSelectWidgetOption>();
    if ((options == null) || (options.size() == 0)) {
      return defaults;
    }
    for (int i = 0; i < options.size(); i++) {
      Object o = options.get(i);
      if (o instanceof CmsSelectWidgetOption) {
        CmsSelectWidgetOption option = (CmsSelectWidgetOption) o;
        if (option.isDefault()) {
          defaults.add(option);
        }
      }
    }
    return defaults;
  }

  /**
   * Parses a widget configuration String for select option values.
   *
   * <p>If the input is <code>null</code> or empty, a <code>{@link Collections#EMPTY_LIST}</code> is
   * returned.
   *
   * <p>Please note: No exception is thrown in case the input is malformed, all malformed entries
   * are silently ignored.
   *
   * <p>
   *
   * @param input the widget input string to parse
   * @return a List of <code>{@link CmsSelectWidgetOption}</code> elements
   */
  public static List<CmsSelectWidgetOption> parseOptions(String input) {

    if (CmsStringUtil.isEmptyOrWhitespaceOnly(input)) {
      // default result for empty input
      return Collections.emptyList();
    }

    // cut along the delimiter
    String[] parts = CmsStringUtil.splitAsArray(input, INPUT_DELIMITER);
    List<CmsSelectWidgetOption> result = new ArrayList<CmsSelectWidgetOption>();

    // indicates if a default of 'true' was already set in this result list
    boolean foundDefault = false;

    for (int i = 0; i < parts.length; i++) {

      String part = parts[i].trim();
      if (part.length() == 0) {
        // skip empty parts
        continue;
      }

      try {

        String value = null;
        String option = null;
        String help = null;
        boolean isDefault = false;

        int posValue = part.indexOf(KEY_VALUE);
        int posDefault = part.indexOf(KEY_DEFAULT);
        int posOption = part.indexOf(KEY_OPTION);
        int posHelp = part.indexOf(KEY_HELP);

        boolean shortValue = false;
        if (posValue < 0) {
          // shortcut syntax, value key must be at first position
          if ((posDefault == 0) || (posOption == 0) || (posHelp == 0)) {
            // malformed part - no value given
            throw new CmsWidgetException(
                Messages.get().container(Messages.ERR_MALFORMED_SELECT_OPTIONS_1, input));
          }
          posValue = 0;
          shortValue = true;
        }

        // a 'value' must be always present
        int end = part.length();
        // check where the 'value' ends
        if (posHelp > posValue) {
          end = posHelp;
        }
        if ((posDefault > posValue) && (posDefault < end)) {
          end = posDefault;
        }
        if ((posOption > posValue) && (posOption < end)) {
          end = posOption;
        }
        if (shortValue) {
          // no explicit setting using the key, value must be at the first position
          value = part.substring(0, end).trim();
        } else {
          value = part.substring(posValue + KEY_VALUE.length(), end).trim();
          // cut of trailing '
          value = value.substring(0, value.length() - 1);
        }

        boolean shortOption = false;
        // check if the option is appended using the ':' shortcut
        if ((shortValue) && (posOption < 0)) {
          int pos = value.indexOf(OPTION_DELIMITER);
          if (pos >= 0) {
            // shortcut syntax is used
            posOption = pos;
            shortOption = true;
            value = value.substring(0, pos);
          }
        }

        if (posDefault >= 0) {
          // there was an explicit 'default' setting using the key, check where it ends
          end = part.length();
          if (posHelp > posDefault) {
            end = posHelp;
          }
          if ((posOption > posDefault) && (posOption < end)) {
            end = posOption;
          }
          if ((posValue > posDefault) && (posValue < end)) {
            end = posValue;
          }
          String sub = part.substring(posDefault + KEY_DEFAULT.length(), end).trim();
          // cut of trailing '
          sub = sub.substring(0, sub.length() - 1);
          isDefault = Boolean.valueOf(sub).booleanValue();
        } else {
          // check for shortcut syntax, value must end with a '*'
          if (value.charAt(value.length() - 1) == DEFAULT_MARKER) {
            isDefault = true;
            value = value.substring(0, value.length() - 1);
          }
        }

        if (posOption >= 0) {
          // an 'option' setting is available, check where it ends
          end = part.length();
          if (posHelp > posOption) {
            end = posHelp;
          }
          if ((posDefault > posOption) && (posDefault < end)) {
            end = posDefault;
          }
          if ((posValue > posOption) && (posValue < end)) {
            end = posValue;
          }
          if (shortOption) {
            // shortcut syntax used for option with ':' appended to value
            option = part.substring(posOption + 1, end).trim();
          } else {
            option = part.substring(posOption + KEY_OPTION.length(), end).trim();
            // cut of trailing '
            option = option.substring(0, option.length() - 1);
          }
        }

        if (posHelp >= 0) {
          // a 'help' setting is available, check where it ends
          end = part.length();
          if (posOption > posHelp) {
            end = posOption;
          }
          if ((posDefault > posHelp) && (posDefault < end)) {
            end = posDefault;
          }
          if ((posValue > posHelp) && (posValue < end)) {
            end = posValue;
          }
          help = part.substring(posHelp + KEY_HELP.length(), end).trim();
          // cut of trailing '
          help = help.substring(0, help.length() - 1);
        }

        // check if there was already a 'true' default, if so all other entries are 'false'
        if (foundDefault) {
          isDefault = false;
        } else if (isDefault) {
          foundDefault = true;
        }

        result.add(new CmsSelectWidgetOption(value, isDefault, option, help));

      } catch (Exception e) {
        if (LOG.isInfoEnabled()) {
          LOG.info(Messages.get().getBundle().key(Messages.ERR_MALFORMED_SELECT_OPTIONS_1, input));
        }
      }
    }

    return result;
  }

  /** @see java.lang.Object#equals(java.lang.Object) */
  @Override
  public boolean equals(Object obj) {

    if (!(obj instanceof CmsSelectWidgetOption)) {
      return false;
    }
    CmsSelectWidgetOption other = (CmsSelectWidgetOption) obj;
    if (m_default != other.m_default) {
      return false;
    }
    if (m_value == null) {
      if (other.m_value != null) {
        return false;
      }
    } else if (!m_value.equals(other.m_value)) {
      return false;
    }
    if (m_option == null) {
      if (other.m_option != null) {
        return false;
      }
    } else if (!m_option.equals(other.m_option)) {
      return false;
    }
    if (m_help == null) {
      if (other.m_help != null) {
        return false;
      }
    } else if (!m_help.equals(other.m_help)) {
      return false;
    }
    return true;
  }

  /**
   * Returns the (optional) help text of this select option.
   *
   * <p>
   *
   * @return the (optional) help text of this select option
   */
  public String getHelp() {

    return m_help;
  }

  /**
   * Returns the option text of this select option.
   *
   * <p>If this has not been set, the result of <code>{@link #getValue()}</code> is returned, there
   * will always be a result other than <code>null</code> returned.
   *
   * <p>
   *
   * @return the option text of this select option
   */
  public String getOption() {

    if (m_option == null) {
      return getValue();
    }
    return m_option;
  }

  /**
   * Returns the value of this select option.
   *
   * <p>
   *
   * @return the value of this select option
   */
  public String getValue() {

    return m_value;
  }

  /** @see java.lang.Object#hashCode() */
  @Override
  public int hashCode() {

    if (m_hashcode == 0) {
      StringBuffer hash = new StringBuffer(128);
      hash.append(m_value);
      hash.append('|');
      hash.append(m_default);
      hash.append('|');
      hash.append(m_option);
      hash.append('|');
      hash.append(m_help);
      m_hashcode = hash.toString().hashCode();
    }
    return m_hashcode;
  }

  /**
   * Returns <code>true</code> if this is the default value of the selection.
   *
   * <p>
   *
   * @return <code>true</code> if this is the default value of the selection
   */
  public boolean isDefault() {

    return m_default;
  }

  /** @see java.lang.Object#toString() */
  @Override
  public String toString() {

    StringBuffer result = new StringBuffer(128);

    result.append(KEY_VALUE);
    result.append(m_value);
    result.append('\'');
    if (m_default) {
      result.append(' ');
      result.append(KEY_DEFAULT);
      result.append(m_default);
      result.append('\'');
    }
    if (m_option != null) {
      result.append(' ');
      result.append(KEY_OPTION);
      result.append(m_option);
      result.append('\'');
    }
    if (m_help != null) {
      result.append(' ');
      result.append(KEY_HELP);
      result.append(m_help);
      result.append('\'');
    }
    return result.toString();
  }
}
/**
 * A factory to create form field instances of a specified type.
 *
 * <p>Additional <code>{@link A_CmsField}</code> implementations may be specified in a file:
 * "custom_form_field.properties" under "WEB-INF/classes/" of your web application. The format has
 * to be as follows:
 *
 * <pre>
 * FIELDS= &lt;fieldtypename&gt;: &lt;fieldtypeclass&gt; [, &lt;fieldtypename&gt;:&lt;fieldtypeclass&gt;]*
 * </pre>
 *
 * where &lt;fieldtypename&gt; is the visible name of the field that will be offered in the XML
 * content editor and &gt;fieldtypeclass&gt; has to be a fully qualified classame of a class that
 * implemets <code>{@link A_CmsField}</code>.
 *
 * <p>
 *
 * @author Thomas Weckert
 * @version $Revision: 1.9 $
 * @since 7.0.4
 */
public final class CmsFieldFactory {

  /** Filename of the optional custom form field properties. */
  public static final String CUSTOM_FORM_FIELD_PROPERTIES =
      OpenCms.getSystemInfo()
          .getAbsoluteRfsPathRelativeToWebInf(
              "classes" + File.separatorChar + "custom_form_field.properties");

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsFieldFactory.class);

  /** The shared instance of the field factory. */
  private static CmsFieldFactory sharedInstance;

  /** The registered field types keyed by their type name. */
  private Map<String, String> m_registeredFieldTypes;

  /**
   * Default constructor.
   *
   * <p>
   */
  private CmsFieldFactory() {

    super();

    m_registeredFieldTypes = new HashMap<String, String>(20);

    // register all the standard OpenCms field types
    registerFieldType(CmsCheckboxField.getStaticType(), CmsCheckboxField.class.getName());
    registerFieldType(CmsEmailField.getStaticType(), CmsEmailField.class.getName());
    registerFieldType(CmsFileUploadField.getStaticType(), CmsFileUploadField.class.getName());
    registerFieldType(CmsHiddenField.getStaticType(), CmsHiddenField.class.getName());
    registerFieldType(CmsRadioButtonField.getStaticType(), CmsRadioButtonField.class.getName());
    registerFieldType(CmsSelectionField.getStaticType(), CmsSelectionField.class.getName());
    registerFieldType(CmsTextField.getStaticType(), CmsTextField.class.getName());
    registerFieldType(CmsTextareaField.getStaticType(), CmsTextareaField.class.getName());
    registerFieldType(CmsEmptyField.getStaticType(), CmsEmptyField.class.getName());
    registerFieldType(CmsPrivacyField.getStaticType(), CmsPrivacyField.class.getName());
    registerFieldType(CmsDynamicField.getStaticType(), CmsDynamicField.class.getName());
    registerFieldType(CmsTableField.getStaticType(), CmsTableField.class.getName());
    registerFieldType(CmsPasswordField.getStaticType(), CmsPasswordField.class.getName());
    registerFieldType(CmsPagingField.getStaticType(), CmsPagingField.class.getName());
    registerFieldType(CmsDisplayField.getStaticType(), CmsDisplayField.class.getName());
    registerFieldType(CmsHiddenDisplayField.getStaticType(), CmsHiddenDisplayField.class.getName());
    registerFieldType(CmsParameterField.getStaticType(), CmsParameterField.class.getName());

    File propertyFile = null;
    try {

      // register all custom field types declared in a property file.
      // since custom fields are optional, the property file doesn't have to exist necessarily in
      // the file system.
      // this file should contain a mapping of field type names to a Java classes separated by a
      // colon ":", e.g.:
      // FIELDS=<fieldtype>:<java class>,...,<fieldtype>:<java class>

      propertyFile = new File(CUSTOM_FORM_FIELD_PROPERTIES);
      if (propertyFile.exists()) {

        ExtendedProperties fieldProperties = new ExtendedProperties();
        fieldProperties.load(new FileInputStream(propertyFile));

        Iterator<String> i = fieldProperties.keySet().iterator();
        while (i.hasNext()) {

          String key = i.next();
          if (!"FIELDS".equalsIgnoreCase(key)) {
            continue;
          }

          String[] values = fieldProperties.getStringArray(key);
          if ((values == null) || (values.length == 0)) {
            continue;
          }

          for (int j = 0, n = values.length; j < n; j++) {

            String field = values[j];
            int index = field.indexOf(":");
            if (index == -1) {
              continue;
            }

            String fieldType = field.substring(0, index);
            String fieldClass = field.substring(index + 1, field.length());
            registerFieldType(fieldType, fieldClass);
          }
        }
      }
    } catch (IOException e) {
      if (LOG.isErrorEnabled()) {
        LOG.error(
            Messages.get()
                .getBundle()
                .key(
                    Messages.LOG_ERR_READING_CUSTOM_FORM_FIELD_PROPERTIES_1,
                    propertyFile == null
                        ? CUSTOM_FORM_FIELD_PROPERTIES
                        : propertyFile.getAbsolutePath()),
            e);
      }
    }
  }

  /**
   * Returns the shared instance of the field factory.
   *
   * <p>
   *
   * @return the shared instance of the field factory
   */
  public static synchronized CmsFieldFactory getSharedInstance() {

    if (sharedInstance == null) {
      sharedInstance = new CmsFieldFactory();
    }

    return sharedInstance;
  }

  /**
   * Returns an instance of a form field of the specified type.
   *
   * <p>
   *
   * @param type the desired type of the form field
   * @return the instance of a form field, or null if creating an instance of the class failed
   */
  public I_CmsField getField(String type) {

    try {
      String className = m_registeredFieldTypes.get(type);
      return (I_CmsField) Class.forName(className).newInstance();
    } catch (Throwable t) {
      if (LOG.isWarnEnabled()) {
        LOG.warn(Messages.get().getBundle().key(Messages.LOG_ERR_FIELD_INSTANTIATION_1, type), t);
      }
    }
    return null;
  }

  /** @see java.lang.Object#finalize() */
  @Override
  protected void finalize() throws Throwable {

    try {
      if (m_registeredFieldTypes != null) {
        m_registeredFieldTypes.clear();
      }
    } catch (Throwable t) {
      // ignore
    }
    super.finalize();
  }

  /**
   * Registers a class as a field type in the factory.
   *
   * <p>
   *
   * @param type the type of the field
   * @param className the name of the field class
   * @return the previous class associated with this type, or null if there was no mapping before
   */
  private Object registerFieldType(String type, String className) {

    return m_registeredFieldTypes.put(type, className);
  }
}
/**
 * A list that displays resources and the existance of their language nodes.
 *
 * <p>
 *
 * @since 7.5.1
 */
public class CmsLanguageCopySelectionList extends A_CmsListDialog {

  /** list action id constant. */
  public static final String LIST_ACTION_NONE = "an";

  /** list column id constant. */
  public static final String LIST_COLUMN_ICON = "lcic";

  /** list action id constant. */
  public static final String LIST_COLUMN_ID = "li.id.languagecopyselection";

  /** list column id constant. */
  public static final String LIST_COLUMN_PATH = "lcp";

  /** list column id constant. */
  public static final String LIST_COLUMN_PREFIX_PROPERTY = "cnp-";

  /** list column id constant. */
  public static final String LIST_COLUMN_RESOURCETYPE = "lcrt";

  /** list item detail id constant. */
  public static final String LIST_DETAIL_FULLPATH = "df";

  /** Multi action for copy. */
  public static final String LIST_MACTION_COPY = "mac";

  /** The request parameter for the paths to work on. */
  public static final String PARAM_PATHS = "paths";

  /** The request parameter for the paths to work on. */
  public static final String PARAM_SIBLINGS = "siblings";

  /** The request parameter for the source language. */
  public static final String PARAM_SOURCE_LANGUAGE = "sourcelanguage";

  /** The request parameter for the target language. */
  public static final String PARAM_TARGET_LANGUAGE = "targetlanguage";

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsLanguageCopySelectionList.class);

  /** The source language. */
  private String m_paramSourcelanguage;

  /** The target language. */
  private String m_paramTargetlanguage;

  /** The paths. */
  private String[] m_paths;

  /**
   * Public constructor.
   *
   * <p>
   *
   * @param jsp an initialized JSP action element
   * @throws CmsException if something goes wrong.
   * @throws FileNotFoundException if something goes wrong.
   */
  public CmsLanguageCopySelectionList(final CmsJspActionElement jsp)
      throws FileNotFoundException, CmsException {

    this(jsp, LIST_COLUMN_ID, Messages.get().container(Messages.GUI_LIST_LANGUAGECOPY_NAME_0));
  }

  /**
   * Public constructor.
   *
   * <p>
   *
   * @param jsp an initialized JSP action element
   * @param listId the id of the list
   * @param listName the list name
   * @throws CmsException if something goes wrong.
   * @throws FileNotFoundException if something goes wrong.
   */
  public CmsLanguageCopySelectionList(
      final CmsJspActionElement jsp, final String listId, final CmsMessageContainer listName)
      throws FileNotFoundException, CmsException {

    this(jsp, listId, listName, LIST_COLUMN_ID, CmsListOrderEnum.ORDER_ASCENDING, null);
  }

  /**
   * Public constructor.
   *
   * <p>
   *
   * @param jsp an initialized JSP action element
   * @param listId the id of the displayed list
   * @param listName the name of the list
   * @param sortedColId the a priory sorted column
   * @param sortOrder the order of the sorted column
   * @param searchableColId the column to search into
   * @throws CmsException if something goes wrong.
   * @throws FileNotFoundException if something goes wrong.
   */
  @SuppressWarnings("unused")
  public CmsLanguageCopySelectionList(
      final CmsJspActionElement jsp,
      final String listId,
      final CmsMessageContainer listName,
      final String sortedColId,
      final CmsListOrderEnum sortOrder,
      final String searchableColId)
      throws FileNotFoundException, CmsException {

    super(jsp, listId, listName, sortedColId, sortOrder, searchableColId);
  }

  /**
   * Public constructor with JSP variables.
   *
   * <p>
   *
   * @param context the JSP page context
   * @param req the JSP request
   * @param res the JSP response
   * @throws CmsException if something goes wrong.
   * @throws FileNotFoundException if something goes wrong.
   */
  public CmsLanguageCopySelectionList(
      final PageContext context, final HttpServletRequest req, final HttpServletResponse res)
      throws FileNotFoundException, CmsException {

    this(new CmsJspActionElement(context, req, res));
  }

  /** @see org.opencms.workplace.list.A_CmsListDialog#executeListMultiActions() */
  @Override
  @SuppressWarnings("unchecked")
  public void executeListMultiActions() throws IOException, ServletException, CmsRuntimeException {

    if (getParamListAction().equals(LIST_MACTION_COPY)) {

      // create absolute RFS path and store it in dialog object
      Map<String, String> params = new HashMap<String, String>();
      List<CmsListItem> items = this.getSelectedItems();
      List<String> paths = new LinkedList<String>();
      for (CmsListItem item : items) {
        paths.add(String.valueOf(item.get(LIST_COLUMN_PATH)));
      }
      params.put(
          CmsLanguageCopyFolderAndLanguageSelectDialog.PARAM_COPYRESOURCES,
          CmsStringUtil.collectionAsString(paths, ","));
      // the source language
      params.put(PARAM_SOURCE_LANGUAGE, getParamSourcelanguage());
      // the target language
      params.put(PARAM_TARGET_LANGUAGE, getParamTargetlanguage());
      // set style to display report in correct layout
      params.put(PARAM_STYLE, CmsToolDialog.STYLE_NEW);
      // set close link to get back to overview after finishing the import
      params.put(PARAM_CLOSELINK, CmsToolManager.linkForToolPath(getJsp(), "/contenttools"));
      // redirect to the report output JSP
      getToolManager()
          .jspForwardPage(
              this,
              CmsWorkplace.PATH_WORKPLACE + "admin/contenttools/languagecopy/report.jsp",
              params);
    }
    listSave();
  }

  /** @see org.opencms.workplace.list.A_CmsListDialog#executeListSingleActions() */
  @SuppressWarnings("unused")
  @Override
  public void executeListSingleActions() throws IOException, ServletException, CmsRuntimeException {

    // do nothing
  }

  /** @return the resourcses to copy */
  @SuppressWarnings("unchecked")
  public String[] getCopyResources() {

    List<CmsListItem> items = this.getSelectedItems();
    String paths = "";
    boolean initial = true;
    for (CmsListItem item : items) {
      if (!initial) {
        paths.concat(",");
      }
      paths.concat(String.valueOf(item.get(LIST_COLUMN_PATH)));
      initial = false;
    }
    return CmsStringUtil.splitAsArray(paths, ",");
  }

  /** @return the paths */
  public String getParamPaths() {

    return CmsStringUtil.arrayAsString(m_paths, ",");
  }

  /** @return the source language */
  public String getParamSourcelanguage() {

    return m_paramSourcelanguage;
  }

  /** @return the target language */
  public String getParamTargetlanguage() {

    return m_paramTargetlanguage;
  }

  /** @param paths the paths to set */
  public void setParamPaths(final String paths) {

    this.m_paths = CmsStringUtil.splitAsArray(paths, ",");
  }

  /** @param sourceLanguage the source language */
  public void setParamSourcelanguage(String sourceLanguage) {

    m_paramSourcelanguage = sourceLanguage;
  }

  /** @param targetLanguage the target language */
  public void setParamTargetlanguage(String targetLanguage) {

    m_paramTargetlanguage = targetLanguage;
  }

  /** @see org.opencms.workplace.list.A_CmsListDialog#fillDetails(java.lang.String) */
  @Override
  protected void fillDetails(final String detailId) {

    // do nothing
  }

  /** @see org.opencms.workplace.list.A_CmsListDialog#getListItems() */
  @Override
  protected List<CmsListItem> getListItems() {

    List<CmsListItem> result = new ArrayList<CmsListItem>();
    // get content
    CmsListItem item;
    int idCounter = 0;
    for (CmsResource resource : this.getResources()) {
      item = getList().newItem(resource.getRootPath());
      this.fillItem(resource, item, idCounter);
      idCounter++;
      result.add(item);
    }
    return result;
  }

  /** @see org.opencms.workplace.CmsWorkplace#initMessages() */
  @Override
  protected void initMessages() {

    // add specific dialog resource bundle
    addMessages(Messages.get().getBundleName());
    // add default resource bundles
    super.initMessages();
  }

  /**
   * @see
   *     org.opencms.workplace.list.A_CmsListDialog#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings,
   *     javax.servlet.http.HttpServletRequest)
   */
  @Override
  protected void initWorkplaceRequestValues(
      final CmsWorkplaceSettings settings, final HttpServletRequest request) {

    super.initWorkplaceRequestValues(settings, request);
  }

  /**
   * @see
   *     org.opencms.workplace.list.A_CmsListDialog#setColumns(org.opencms.workplace.list.CmsListMetadata)
   */
  @SuppressWarnings("unchecked")
  @Override
  protected void setColumns(final CmsListMetadata metadata) {

    // enforce re-invocation of this method because columns are varying and must not be cached:
    metadata.setVolatile(true);

    // add column for icon
    CmsListColumnDefinition iconCol = new CmsListColumnDefinition(LIST_COLUMN_ICON);
    iconCol.setName(Messages.get().container(Messages.GUI_LIST_LANGUAGECOPY_COL_ICON_NAME_0));
    iconCol.setHelpText(Messages.get().container(Messages.GUI_LIST_LANGUAGECOPY_COL_ICON_HELP_0));
    iconCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
    iconCol.setWidth("16");
    iconCol.setSorteable(false);
    metadata.addColumn(iconCol);
    iconCol.setPrintable(true);

    // add column for name
    CmsListColumnDefinition nameCol = new CmsListColumnDefinition(LIST_COLUMN_PATH);
    nameCol.setName(Messages.get().container(Messages.GUI_LIST_LANGUAGECOPY_COL_PATH_NAME_0));
    nameCol.setHelpText(Messages.get().container(Messages.GUI_LIST_LANGUAGECOPY_COL_PATH_HELP_0));
    nameCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
    nameCol.setSorteable(true);
    metadata.addColumn(nameCol);
    nameCol.setPrintable(true);

    // add column for resource type
    CmsListColumnDefinition typeCol = new CmsListColumnDefinition(LIST_COLUMN_RESOURCETYPE);
    typeCol.setName(
        Messages.get().container(Messages.GUI_LIST_LANGUAGECOPY_COL_RESOURCETYPE_NAME_0));
    typeCol.setHelpText(
        Messages.get().container(Messages.GUI_LIST_LANGUAGECOPY_COL_RESOURCETYPE_HELP_0));
    typeCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
    typeCol.setSorteable(true);
    metadata.addColumn(typeCol);
    typeCol.setPrintable(true);

    // add columns for languages:
    List<Locale> sysLocales = OpenCms.getLocaleManager().getAvailableLocales();
    CmsListColumnDefinition langCol;
    for (Locale locale : sysLocales) {
      langCol = new CmsListColumnDefinition(locale.toString());
      langCol.setName(
          Messages.get()
              .container(
                  Messages.GUI_LIST_LANGUAGECOPY_COL_LANGUAGE_NAME_1,
                  new Object[] {locale.toString()}));
      langCol.setHelpText(
          Messages.get().container(Messages.GUI_LIST_LANGUAGECOPY_COL_LANGUAGE_HELP_0));
      langCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
      langCol.setSorteable(false);
      metadata.addColumn(langCol);
      langCol.setPrintable(true);
    }
  }

  /**
   * @see
   *     org.opencms.workplace.list.A_CmsListDialog#setIndependentActions(org.opencms.workplace.list.CmsListMetadata)
   */
  @Override
  protected void setIndependentActions(final CmsListMetadata metadata) {

    // nothing to do here
  }

  /**
   * @see
   *     org.opencms.workplace.list.A_CmsListDialog#setMultiActions(org.opencms.workplace.list.CmsListMetadata)
   */
  @Override
  protected void setMultiActions(final CmsListMetadata metadata) {

    // add copy multi action
    CmsListMultiAction deleteMultiAction = new CmsListMultiAction(LIST_MACTION_COPY);
    deleteMultiAction.setName(
        Messages.get().container(Messages.GUI_LIST_SEARCHINDEX_MACTION_COPY_NAME_0));
    deleteMultiAction.setHelpText(
        Messages.get().container(Messages.GUI_LIST_SEARCHINDEX_MACTION_COPY_HELP_0));
    deleteMultiAction.setIconPath(ICON_MULTI_ADD);
    metadata.addMultiAction(deleteMultiAction);
  }

  /**
   * Fills a single item.
   *
   * <p>
   *
   * @param resource the corresponding resource.
   * @param item the item to fill.
   * @param id used for the ID column.
   */
  @SuppressWarnings("unchecked")
  private void fillItem(final CmsResource resource, final CmsListItem item, final int id) {

    CmsObject cms = this.getCms();
    CmsXmlContent xmlContent;

    I_CmsResourceType type;
    String iconPath;

    // fill path column:
    String sitePath = cms.getSitePath(resource);
    item.set(LIST_COLUMN_PATH, sitePath);

    // fill language node existence column:
    item.set(LIST_COLUMN_PATH, sitePath);
    boolean languageNodeExists = false;
    String languageNodeHtml;

    List<Locale> sysLocales = OpenCms.getLocaleManager().getAvailableLocales();
    try {
      xmlContent = CmsXmlContentFactory.unmarshal(cms, cms.readFile(resource));
      for (Locale locale : sysLocales) {
        languageNodeExists = xmlContent.hasLocale(locale);
        if (languageNodeExists) {
          languageNodeHtml = "<input type=\"checkbox\" checked=\"checked\" disabled=\"disabled\"/>";
        } else {
          languageNodeHtml = "<input type=\"checkbox\" disabled=\"disabled\"/>";
        }
        item.set(locale.toString(), languageNodeHtml);
      }
    } catch (Throwable e1) {
      LOG.error(
          Messages.get().getBundle().key(Messages.LOG_ERR_LANGUAGECOPY_DETERMINE_LANGUAGE_NODE_1),
          e1);
      languageNodeHtml = "n/a";
      for (Locale locale : sysLocales) {
        item.set(locale.toString(), languageNodeHtml);
      }
    }

    // type column:
    type = OpenCms.getResourceManager().getResourceType(resource);
    item.set(LIST_COLUMN_RESOURCETYPE, type.getTypeName());

    // icon column with title property for tooltip:
    String title = "";
    try {
      CmsProperty titleProperty =
          cms.readPropertyObject(resource, CmsPropertyDefinition.PROPERTY_TITLE, true);
      title = titleProperty.getValue();
    } catch (CmsException e) {
      LOG.warn(Messages.get().getBundle().key(Messages.LOG_WARN_LANGUAGECOPY_READPROP_1), e);
    }

    iconPath =
        getSkinUri()
            + CmsWorkplace.RES_PATH_FILETYPES
            + OpenCms.getWorkplaceManager().getExplorerTypeSetting(type.getTypeName()).getIcon();
    String iconImage;
    iconImage =
        "<img src=\""
            + iconPath
            + "\" alt=\""
            + type.getTypeName()
            + "\" title=\""
            + title
            + "\" />";
    item.set(LIST_COLUMN_ICON, iconImage);
  }

  /**
   * Reads the resources available for processing based on the path parameters.
   *
   * <p>
   *
   * @return the resources available for processing based on the path parameters.
   */
  @SuppressWarnings("unchecked")
  private List<CmsResource> getResources() {

    List<CmsResource> result = new LinkedList<CmsResource>();
    CmsObject cms = this.getCms();
    CmsResourceFilter filter = CmsResourceFilter.ALL;
    try {
      for (String path : this.m_paths) {
        List<CmsResource> resources = cms.readResources(path, filter, true);
        // filter out any resource that is no XML content:
        for (CmsResource resource : resources) {
          if (resource.isFile()) {
            if (CmsResourceTypeXmlContent.isXmlContent(resource)) {
              result.add(resource);
            } else if (CmsResourceTypeXmlPage.isXmlPage(resource)) {
              result.add(resource);
            }
          }
        }
      }
    } catch (CmsException e) {
      LOG.error(Messages.get().getBundle().key(Messages.LOG_ERR_LANGUAGECOPY_READRESOURCES_0), e);
      result = Collections.emptyList();
    }

    return result;
  }
}
/**
 * Defines a named menu rule set to check the visibility of a single context menu item in the
 * explorer view.
 *
 * <p>
 *
 * @since 6.5.6
 */
public class CmsMenuRule {

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsMenuRule.class);

  /** Indicates if the menu rule set is frozen. */
  private boolean m_frozen;

  /** The menu item rules that are part of this rule set. */
  private List<I_CmsMenuItemRule> m_menuItemRules;

  /** The name of the menu rule set. */
  private String m_name;

  /**
   * Constructor without parameters, needed for initialization from OpenCms configuration.
   *
   * <p>
   */
  public CmsMenuRule() {

    // initialize members
    m_menuItemRules = new ArrayList<I_CmsMenuItemRule>(5);
  }

  /**
   * Adds a single menu item rule to the list of rules.
   *
   * <p>
   *
   * @param menuItemRule the menu item rule to add
   */
  public void addMenuItemRule(I_CmsMenuItemRule menuItemRule) {

    try {
      m_menuItemRules.add(menuItemRule);
    } catch (Exception e) {
      if (LOG.isErrorEnabled()) {
        LOG.error(
            Messages.get()
                .getBundle()
                .key(Messages.ERR_INITIALIZE_MENUITEMRULE_1, menuItemRule.getClass().getName()));
      }
    }
  }

  /**
   * Adds a single menu item rule to the list of rules.
   *
   * <p>
   *
   * @param className the class name of the menu item rule to add
   */
  public void addMenuItemRuleName(String className) {

    try {
      m_menuItemRules.add((I_CmsMenuItemRule) Class.forName(className).newInstance());
    } catch (Exception e) {
      if (LOG.isErrorEnabled()) {
        LOG.error(
            Messages.get().getBundle().key(Messages.ERR_INITIALIZE_MENUITEMRULE_1, className));
      }
    }
  }

  /**
   * Freezes the name and the items of the menu rule set.
   *
   * <p>They can not be modified anymore after calling this method.
   *
   * <p>
   */
  public void freeze() {

    if (!m_frozen) {
      m_frozen = true;
      // freeze the item rules list
      m_menuItemRules = Collections.unmodifiableList(m_menuItemRules);
    }
  }

  /**
   * Returns the first matching rule for the resource to create the context menu for.
   *
   * <p>
   *
   * @param cms the current OpenCms user context
   * @param resourceUtil the initialized resource utilities of the resource
   * @return the first matching rule for the resource
   */
  public I_CmsMenuItemRule getMatchingRule(CmsObject cms, CmsResourceUtil[] resourceUtil) {

    Iterator<I_CmsMenuItemRule> i = getMenuItemRules().iterator();
    while (i.hasNext()) {
      I_CmsMenuItemRule rule = i.next();
      if (rule.matches(cms, resourceUtil)) {
        return rule;
      }
    }
    return null;
  }

  /**
   * Returns the menu item rules class instances.
   *
   * <p>
   *
   * @return the menu item rules class instances
   */
  public List<I_CmsMenuItemRule> getMenuItemRules() {

    return m_menuItemRules;
  }

  /**
   * Returns the name of the menu rule set.
   *
   * <p>
   *
   * @return the name of the menu rule set
   */
  public String getName() {

    return m_name;
  }

  /**
   * Returns <code>true</code> if this menu rule set is frozen, that is read only.
   *
   * <p>
   *
   * @return <code>true</code> if this menu rule set is frozen, that is read only
   */
  public boolean isFrozen() {

    return m_frozen;
  }

  /**
   * Sets the menu item rules class instances.
   *
   * <p>
   *
   * @param menuItemRules the menu item rules class instances
   */
  public void setMenuItemRules(List<I_CmsMenuItemRule> menuItemRules) {

    m_menuItemRules = menuItemRules;
  }

  /**
   * Sets the name of the menu rule set.
   *
   * <p>
   *
   * @param name the name of the menu rule set
   */
  public void setName(String name) {

    if (m_frozen) {
      throw new CmsRuntimeException(Messages.get().container(Messages.ERR_MENURULE_FROZEN_0));
    }
    m_name = name;
  }
}
/**
 * Provides a widget that creates a rich input field using the matching component, for use on a
 * widget dialog.
 *
 * <p>The matching component is determined by checking the installed editors for the best matching
 * component to use.
 *
 * <p>
 *
 * @since 6.0.1
 */
public class CmsHtmlWidget extends A_CmsHtmlWidget {

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsHtmlWidget.class);

  /**
   * The editor widget to use depending on the current users settings, current browser and installed
   * editors.
   */
  private I_CmsWidget m_editorWidget;

  /**
   * Creates a new html editing widget.
   *
   * <p>
   */
  public CmsHtmlWidget() {

    // empty constructor is required for class registration
    this("");
  }

  /**
   * Creates a new html editing widget with the given configuration.
   *
   * <p>
   *
   * @param configuration the configuration to use
   */
  public CmsHtmlWidget(CmsHtmlWidgetOption configuration) {

    super(configuration);
  }

  /**
   * Creates a new html editing widget with the given configuration.
   *
   * <p>
   *
   * @param configuration the configuration to use
   */
  public CmsHtmlWidget(String configuration) {

    super(configuration);
  }

  /**
   * @see org.opencms.widgets.I_CmsWidget#getDialogIncludes(org.opencms.file.CmsObject,
   *     org.opencms.widgets.I_CmsWidgetDialog)
   */
  public String getDialogIncludes(CmsObject cms, I_CmsWidgetDialog widgetDialog) {

    return getEditorWidget(cms, widgetDialog).getDialogIncludes(cms, widgetDialog);
  }

  /**
   * @see org.opencms.widgets.I_CmsWidget#getDialogInitCall(org.opencms.file.CmsObject,
   *     org.opencms.widgets.I_CmsWidgetDialog)
   */
  public String getDialogInitCall(CmsObject cms, I_CmsWidgetDialog widgetDialog) {

    return getEditorWidget(cms, widgetDialog).getDialogInitCall(cms, widgetDialog);
  }

  /**
   * @see org.opencms.widgets.I_CmsWidget#getDialogInitMethod(org.opencms.file.CmsObject,
   *     org.opencms.widgets.I_CmsWidgetDialog)
   */
  public String getDialogInitMethod(CmsObject cms, I_CmsWidgetDialog widgetDialog) {

    return getEditorWidget(cms, widgetDialog).getDialogInitMethod(cms, widgetDialog);
  }

  /**
   * @see org.opencms.widgets.I_CmsWidget#getDialogWidget(org.opencms.file.CmsObject,
   *     org.opencms.widgets.I_CmsWidgetDialog, org.opencms.widgets.I_CmsWidgetParameter)
   */
  public String getDialogWidget(
      CmsObject cms, I_CmsWidgetDialog widgetDialog, I_CmsWidgetParameter param) {

    return getEditorWidget(cms, widgetDialog).getDialogWidget(cms, widgetDialog, param);
  }

  /** @see org.opencms.widgets.I_CmsWidget#newInstance() */
  public I_CmsWidget newInstance() {

    return new CmsHtmlWidget(getConfiguration());
  }

  /**
   * @see org.opencms.widgets.I_CmsWidget#setEditorValue(org.opencms.file.CmsObject, java.util.Map,
   *     org.opencms.widgets.I_CmsWidgetDialog, org.opencms.widgets.I_CmsWidgetParameter)
   */
  public void setEditorValue(
      CmsObject cms,
      Map formParameters,
      I_CmsWidgetDialog widgetDialog,
      I_CmsWidgetParameter param) {

    String[] values = (String[]) formParameters.get(param.getId());
    if ((values != null) && (values.length > 0)) {
      String val = CmsEncoder.decode(values[0], CmsEncoder.ENCODING_UTF_8);
      param.setStringValue(cms, val);
    }
  }

  /**
   * Returns the editor widget to use depending on the current users settings, current browser and
   * installed editors.
   *
   * <p>
   *
   * @param cms the current CmsObject
   * @param widgetDialog the dialog where the widget is used on
   * @return the editor widget to use depending on the current users settings, current browser and
   *     installed editors
   */
  private I_CmsWidget getEditorWidget(CmsObject cms, I_CmsWidgetDialog widgetDialog) {

    if (m_editorWidget == null) {
      // get HTML widget to use from editor manager
      String widgetClassName =
          OpenCms.getWorkplaceManager()
              .getWorkplaceEditorManager()
              .getWidgetEditor(cms.getRequestContext(), widgetDialog.getUserAgent());
      boolean foundWidget = true;
      if (CmsStringUtil.isEmpty(widgetClassName)) {
        // no installed widget found, use default text area to edit HTML value
        widgetClassName = CmsTextareaWidget.class.getName();
        foundWidget = false;
      }
      try {
        if (foundWidget) {
          // get widget instance and set the widget configuration
          Class widgetClass = Class.forName(widgetClassName);
          A_CmsHtmlWidget editorWidget = (A_CmsHtmlWidget) widgetClass.newInstance();
          editorWidget.setHtmlWidgetOption(getHtmlWidgetOption());
          m_editorWidget = editorWidget;
        } else {
          // set the text area to display 15 rows for editing
          Class widgetClass = Class.forName(widgetClassName);
          I_CmsWidget editorWidget = (I_CmsWidget) widgetClass.newInstance();
          editorWidget.setConfiguration("15");
          m_editorWidget = editorWidget;
        }
      } catch (Exception e) {
        // failed to create widget instance
        LOG.error(
            Messages.get()
                .container(Messages.LOG_CREATE_HTMLWIDGET_INSTANCE_FAILED_1, widgetClassName)
                .key());
      }
    }
    return m_editorWidget;
  }
}
/**
 * Provides methods for the comment images dialog on image gallery folders.
 *
 * <p>The following files use this class:
 *
 * <ul>
 *   <li>/commons/commentimages.jsp
 * </ul>
 *
 * <p>
 *
 * @since 6.1.3
 */
public class CmsCommentImages extends CmsDialog {

  /** Value for the action: comment images. */
  public static final int ACTION_COMMENTIMAGES = 100;

  /** The dialog type. */
  public static final String DIALOG_TYPE = "commentimages";

  /** The input field prefix for description property fields. */
  public static final String PREFIX_DESCRIPTION = "desc_";

  /** The input field prefix for title property fields. */
  public static final String PREFIX_TITLE = "title_";

  /** The height of the dialog thumbnails. */
  public static final int THUMB_HEIGHT = 150;

  /** The width of the dialog thumbnails. */
  public static final int THUMB_WIDTH = 200;

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsCommentImages.class);

  /** The image scaler object used in the dialog input form. */
  private CmsImageScaler m_imageScaler;

  /**
   * Public constructor with JSP action element.
   *
   * <p>
   *
   * @param jsp an initialized JSP action element
   */
  public CmsCommentImages(CmsJspActionElement jsp) {

    super(jsp);
  }

  /**
   * Public constructor with JSP variables.
   *
   * <p>
   *
   * @param context the JSP page context
   * @param req the JSP request
   * @param res the JSP response
   */
  public CmsCommentImages(PageContext context, HttpServletRequest req, HttpServletResponse res) {

    this(new CmsJspActionElement(context, req, res));
  }

  /**
   * Performs the comment images action, will be called by the JSP page.
   *
   * <p>
   *
   * @throws JspException if problems including sub-elements occur
   */
  public void actionCommentImages() throws JspException {

    // save initialized instance of this class in request attribute for included sub-elements
    getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
    try {
      performDialogOperation();
      // if no exception is caused comment operation was successful
      actionCloseDialog();
    } catch (Throwable e) {
      // error during rename images, show error dialog
      includeErrorpage(this, e);
    }
  }

  /**
   * Returns the HTML for the dialog input form to comment the images.
   *
   * <p>
   *
   * @return the HTML for the dialog input form to comment the images
   */
  public String buildDialogForm() {

    StringBuffer result = new StringBuffer(16384);
    Iterator i = getImages().iterator();

    result.append("<div style=\"height: 450px; padding: 4px; overflow: auto;\">");

    while (i.hasNext()) {
      CmsResource res = (CmsResource) i.next();
      String imageName = res.getName();
      String propertySuffix = "" + imageName.hashCode();
      result.append(dialogBlockStart(imageName));
      result.append("<table border=\"0\">\n");
      result.append("<tr>\n\t<td style=\"vertical-align: top;\">");
      // create image tag
      result.append("<img src=\"");
      StringBuffer link = new StringBuffer(256);
      link.append(getCms().getSitePath(res));
      link.append(getImageScaler().toRequestParam());
      result.append(getJsp().link(link.toString()));
      result.append("\" border=\"0\" alt=\"\" width=\"");
      result.append(getImageScaler().getWidth());
      result.append("\" height=\"");
      result.append(getImageScaler().getHeight());
      result.append("\">");

      result.append("</td>\n");
      result.append("\t<td class=\"maxwidth\" style=\"vertical-align: top;\">\n");

      result.append("\t\t<table border=\"0\">\n");

      // build title property input row
      String title = "";
      try {
        title =
            getCms()
                .readPropertyObject(res, CmsPropertyDefinition.PROPERTY_TITLE, false)
                .getValue();
      } catch (CmsException e) {
        // log, should never happen
        if (LOG.isErrorEnabled()) {
          LOG.error(e.getLocalizedMessage(getLocale()));
        }
      }
      result.append("\t\t<tr>\n\t\t\t<td style=\"white-space: nowrap;\" unselectable=\"on\">");
      result.append(key(Messages.GUI_LABEL_TITLE_0));
      result.append(":</td>\n\t\t\t<td class=\"maxwidth\">");
      result.append("<input type=\"text\" class=\"maxwidth\" name=\"");
      result.append(PREFIX_TITLE);
      result.append(propertySuffix);
      result.append("\" value=\"");
      if (CmsStringUtil.isNotEmpty(title)) {
        result.append(CmsEncoder.escapeXml(title));
      }
      result.append("\">");
      result.append("</td>\n\t\t</tr>\n");

      // build description property input row
      String description = "";
      try {
        description =
            getCms()
                .readPropertyObject(res, CmsPropertyDefinition.PROPERTY_DESCRIPTION, false)
                .getValue();
      } catch (CmsException e) {
        // log, should never happen
        if (LOG.isErrorEnabled()) {
          LOG.error(e.getLocalizedMessage(getLocale()));
        }
      }
      result.append(
          "\t\t<tr>\n\t\t\t<td style=\"white-space: nowrap; vertical-align: top;\" unselectable=\"on\">");
      result.append(key(Messages.GUI_LABEL_DESCRIPTION_0));
      result.append(":</td>\n\t\t\t<td style=\"vertical-align: top; height: 110px;\">");
      result.append("<textarea rows=\"8\" class=\"maxwidth\" style=\"overflow: auto;\" name=\"");
      result.append(PREFIX_DESCRIPTION);
      result.append(propertySuffix);
      result.append("\">");
      if (CmsStringUtil.isNotEmpty(description)) {
        result.append(CmsEncoder.escapeXml(description));
      }
      result.append("</textarea>");
      result.append("</td>\n\t\t</tr>\n");

      result.append("\t\t</table>\n");

      result.append("</td>\n</tr>\n");
      result.append("</table>\n");
      result.append(dialogBlockEnd());

      if (i.hasNext()) {
        // append spacer if another entry follows
        result.append(dialogSpacer());
      }
    }

    result.append("</div>");

    return result.toString();
  }

  /**
   * Returns the image resources of the gallery folder which are edited in the dialog form.
   *
   * <p>
   *
   * @return the images of the gallery folder which are edited in the dialog form
   */
  protected List getImages() {

    // get all image resources of the folder
    int imageId;
    try {
      imageId =
          OpenCms.getResourceManager()
              .getResourceType(CmsResourceTypeImage.getStaticTypeName())
              .getTypeId();
    } catch (CmsLoaderException e1) {
      // should really never happen
      LOG.warn(e1.getLocalizedMessage(), e1);
      imageId = CmsResourceTypeImage.getStaticTypeId();
    }
    CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION.addRequireType(imageId);
    try {
      return getCms().readResources(getParamResource(), filter, false);
    } catch (CmsException e) {
      // log, should never happen
      if (LOG.isErrorEnabled()) {
        LOG.error(e.getLocalizedMessage(getLocale()));
      }
      return new ArrayList(0);
    }
  }

  /**
   * Returns the initialized image scaler object used to generate thumbnails for the dialog form.
   *
   * <p>
   *
   * @return the initialized image scaler object used to generate thumbnails for the dialog form
   */
  protected CmsImageScaler getImageScaler() {

    if (m_imageScaler == null) {
      // not initialized, create image scaler with default settings
      m_imageScaler = new CmsImageScaler();
      m_imageScaler.setWidth(THUMB_WIDTH);
      m_imageScaler.setHeight(THUMB_HEIGHT);
      m_imageScaler.setRenderMode(Simapi.RENDER_SPEED);
      m_imageScaler.setColor(new Color(0, 0, 0));
      m_imageScaler.setType(1);
    }
    return m_imageScaler;
  }

  /**
   * @see
   *     org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings,
   *     javax.servlet.http.HttpServletRequest)
   */
  protected void initWorkplaceRequestValues(
      CmsWorkplaceSettings settings, HttpServletRequest request) {

    // fill the parameter values in the get/set methods
    fillParamValues(request);

    // check the required permissions to rename the resource
    if (!checkResourcePermissions(CmsPermissionSet.ACCESS_WRITE, false)) {
      // no write permissions for the resource, set cancel action to close dialog
      setParamAction(DIALOG_CANCEL);
    }

    // set the dialog type
    setParamDialogtype(DIALOG_TYPE);
    // set the action for the JSP switch
    if (DIALOG_TYPE.equals(getParamAction())) {
      setAction(ACTION_COMMENTIMAGES);
    } else if (DIALOG_LOCKS_CONFIRMED.equals(getParamAction())) {
      setAction(ACTION_LOCKS_CONFIRMED);
    } else if (DIALOG_CANCEL.equals(getParamAction())) {
      setAction(ACTION_CANCEL);
    } else {
      setAction(ACTION_DEFAULT);
      // build title for comment images dialog
      Object[] args = new Object[] {getParamResource()};
      setParamTitle(key(Messages.GUI_COMMENTIMAGES_TITLE_1, args));
    }
  }

  /**
   * Performs the comment images operation.
   *
   * <p>
   *
   * @return true, if the resources were successfully processed, otherwise false
   * @throws CmsException if commenting is not successful
   */
  protected boolean performDialogOperation() throws CmsException {

    // lock the image gallery folder
    checkLock(getParamResource());

    Iterator i = getImages().iterator();
    // loop over all image resources to change the properties
    while (i.hasNext()) {
      CmsResource res = (CmsResource) i.next();
      String imageName = res.getName();
      String propertySuffix = "" + imageName.hashCode();

      // update the title property
      CmsProperty titleProperty =
          getCms().readPropertyObject(res, CmsPropertyDefinition.PROPERTY_TITLE, false);
      String newValue = getJsp().getRequest().getParameter(PREFIX_TITLE + propertySuffix);
      writeProperty(res, CmsPropertyDefinition.PROPERTY_TITLE, newValue, titleProperty);

      // update the description property
      CmsProperty descProperty =
          getCms().readPropertyObject(res, CmsPropertyDefinition.PROPERTY_DESCRIPTION, false);
      newValue = getJsp().getRequest().getParameter(PREFIX_DESCRIPTION + propertySuffix);
      writeProperty(res, CmsPropertyDefinition.PROPERTY_DESCRIPTION, newValue, descProperty);
    }

    return true;
  }

  /**
   * Writes a property value for a resource, if the value was changed.
   *
   * <p>
   *
   * @param res the resource to write the property to
   * @param propName the name of the property definition
   * @param propValue the new value of the property
   * @param currentProperty the old property object
   * @throws CmsException if something goes wrong
   */
  protected void writeProperty(
      CmsResource res, String propName, String propValue, CmsProperty currentProperty)
      throws CmsException {

    // check if current property is not the null property
    if (currentProperty.isNullProperty()) {
      // create new property object
      currentProperty = new CmsProperty();
      currentProperty.setName(propName);
    }

    if (CmsStringUtil.isEmptyOrWhitespaceOnly(propValue)) {
      // parameter is empty, determine the value to delete
      boolean writeProperty = false;
      if (currentProperty.getStructureValue() != null) {
        currentProperty.setStructureValue(CmsProperty.DELETE_VALUE);
        currentProperty.setResourceValue(null);
        writeProperty = true;
      } else if (currentProperty.getResourceValue() != null) {
        currentProperty.setResourceValue(CmsProperty.DELETE_VALUE);
        currentProperty.setStructureValue(null);
        writeProperty = true;
      }
      if (writeProperty) {
        // write the updated property object
        getCms().writePropertyObject(getCms().getSitePath(res), currentProperty);
      }
    } else {
      // parameter is not empty, check if the value has changed
      if (!propValue.equals(currentProperty.getValue())) {
        if ((currentProperty.getStructureValue() == null)
            && (currentProperty.getResourceValue() == null)) {
          // new property, determine setting from OpenCms workplace configuration
          if (OpenCms.getWorkplaceManager().isDefaultPropertiesOnStructure()) {
            currentProperty.setStructureValue(propValue);
            currentProperty.setResourceValue(null);
          } else {
            currentProperty.setResourceValue(propValue);
            currentProperty.setStructureValue(null);
          }
        } else if (currentProperty.getStructureValue() != null) {
          // structure value has to be updated
          currentProperty.setStructureValue(propValue);
          currentProperty.setResourceValue(null);
        } else {
          // resource value has to be updated
          currentProperty.setResourceValue(propValue);
          currentProperty.setStructureValue(null);
        }
        // write the updated property object
        getCms().writePropertyObject(getCms().getSitePath(res), currentProperty);
      }
    }
  }
}
/**
 * Provides methods to create a special list of CmsDocument objects regarding language and
 * attachment functionality.
 *
 * <p>After adding all documents to the list, the {@link #closeList()} method has to be called once
 * before working with the list contents, e.g. before sorting the list or iterating it.
 *
 * <p>
 *
 * @author Andreas Zahner
 * @version $Revision: 1.3 $
 * @since 6.2.0
 */
public class CmsDocumentList extends AbstractList<CmsDocument> {

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsDocumentList.class);

  private List<CmsDocument> m_attachments;

  private String m_defaultType;

  private CmsDocument[] m_documents;

  private boolean m_onlyVersions;

  private int m_size;

  private boolean m_useAttachments;

  private boolean m_useLanguages;

  private boolean m_useTypes;

  /**
   * Creates an empty document list with an initial capacity of 16 items.
   *
   * <p>
   */
  public CmsDocumentList() {

    this(16);
  }

  /**
   * Creates an empty document list with the flags to use language versions and attachments, to show
   * only versions and the given initial capacity.
   *
   * <p>
   *
   * @param useAttachments the flag is attachments of a document should be conbsidered
   * @param useLanguages the flag if language versions of a document should be considered
   * @param onlyVersions the flag if only versions of a specific document should be added to the
   *     list
   * @param initialCapacity the initial capacity of the document list
   */
  public CmsDocumentList(
      boolean useAttachments, boolean useLanguages, boolean onlyVersions, int initialCapacity) {

    this(false, null, useAttachments, useLanguages, onlyVersions, initialCapacity);
  }

  /**
   * Creates an empty document list with the flags to use language versions, to show only versions
   * and the given initial capacity.
   *
   * <p>
   *
   * @param useLanguages the flag if language versions of a document should be considered
   * @param onlyVersions the flag if only versions of a specific document should be added to the
   *     list
   * @param initialCapacity the initial capacity of the document list
   */
  public CmsDocumentList(boolean useLanguages, boolean onlyVersions, int initialCapacity) {

    this(true, useLanguages, onlyVersions, initialCapacity);
  }

  /**
   * Creates an empty document list with the flags to use language versions and attachments, to show
   * only versions and the given initial capacity.
   *
   * <p>
   *
   * @param useTypes the flag if different types of a document should be considered
   * @param defaultType if list uses types, the default type is specified here
   * @param useAttachments the flag if attachments of a document should be conbsidered
   * @param useLanguages the flag if language versions of a document should be considered
   * @param onlyVersions the flag if only versions of a specific document should be added to the
   *     list
   * @param initialCapacity the initial capacity of the document list
   */
  public CmsDocumentList(
      boolean useTypes,
      String defaultType,
      boolean useAttachments,
      boolean useLanguages,
      boolean onlyVersions,
      int initialCapacity) {

    m_documents = new CmsDocument[initialCapacity];
    m_size = 0;
    m_attachments = new ArrayList<CmsDocument>();
    m_useLanguages = useLanguages;
    m_onlyVersions = onlyVersions;
    m_useAttachments = useAttachments;
    m_useTypes = useTypes;
    m_defaultType = defaultType;
  }

  /**
   * Creates an empty document list with the given initial capacity.
   *
   * <p>
   *
   * @param initialCapacity the initial capacity of the document list
   */
  public CmsDocumentList(int initialCapacity) {

    this(false, false, initialCapacity);
  }

  /** @see java.util.AbstractList#add(java.lang.Object) */
  @Override
  public boolean add(CmsDocument element) {

    CmsDocument doc = convertDocument(element);
    if (doc.isNullDocument() || checkContainer(doc)) {
      return true;
    }

    ensureCapacity(m_size + 1);
    m_documents[m_size++] = doc;
    return true;
  }

  /** @see java.util.AbstractList#add(int, java.lang.Object) */
  @Override
  public void add(int index, CmsDocument element) {

    rangeCheck(index);
    CmsDocument doc = convertDocument(element);
    if (doc.isNullDocument() || checkContainer(doc)) {
      return;
    }

    ensureCapacity(m_size + 1); // Increments modCount!!
    System.arraycopy(m_documents, index, m_documents, index + 1, m_size - index);
    m_documents[index] = doc;
    m_size++;
  }

  /** @see java.util.Collection#clear() */
  @Override
  public void clear() {

    modCount++;
    for (int i = 0; i < m_size; i++) {
      m_documents[i] = null;
    }
    m_size = 0;
  }

  /**
   * Closes the list after adding all documents to it, this is needed to assign the found
   * attachments to the documents.
   *
   * <p>
   */
  public void closeList() {

    for (int i = 0; i < m_attachments.size(); i++) {
      // loop through all attachments
      CmsDocument att = m_attachments.get(i);
      boolean added = false;
      for (int k = 0; k < m_size; k++) {
        // check if the attachment belongs to a document
        if (m_documents[k].isVersionOf(att)) {
          // found a document for the attachment
          if (m_documents[k].addAttachment(att)) {
            added = true;
            break;
          }
        }
      }
      if (!added) {
        // found no document for the attachment, add it as separate attachment to the list
        ensureCapacity(m_size + 1);
        m_documents[m_size++] = att;
      }
    }
    // empty the attachment list
    m_attachments = new ArrayList<CmsDocument>();
  }

  /** @see java.util.Collection#contains(java.lang.Object) */
  @Override
  public boolean contains(Object elem) {

    return indexOf(elem) >= 0;
  }

  /**
   * Increases the capacity of this <tt>CmsDocumentList</tt> instance, if necessary, to ensure that
   * it can hold at least the number of elements specified by the minimum capacity argument.
   *
   * @param minCapacity the desired minimum capacity.
   */
  public void ensureCapacity(int minCapacity) {

    modCount++;
    int oldCapacity = m_documents.length;
    if (minCapacity > oldCapacity) {
      CmsDocument[] oldData = m_documents;
      int newCapacity = ((oldCapacity * 3) / 2) + 1;
      if (newCapacity < minCapacity) {
        newCapacity = minCapacity;
      }
      m_documents = new CmsDocument[newCapacity];
      System.arraycopy(oldData, 0, m_documents, 0, m_size);
    }
  }

  /** @see java.util.AbstractList#get(int) */
  @Override
  public CmsDocument get(int index) {

    rangeCheck(index);
    return m_documents[index];
  }

  /** @see java.util.List#indexOf(java.lang.Object) */
  @Override
  public int indexOf(Object elem) {

    if (elem == null) {
      for (int i = 0; i < m_size; i++) {
        if (m_documents[i] == null) {
          return i;
        }
      }
    } else {
      for (int i = 0; i < m_size; i++) {
        if (elem.equals(m_documents[i])) {
          return i;
        }
      }
    }
    return -1;
  }

  /** @see java.util.AbstractList#remove(int) */
  @Override
  public CmsDocument remove(int index) {

    rangeCheck(index);
    modCount++;
    CmsDocument oldValue = m_documents[index];
    int numMoved = m_size - index - 1;
    if (numMoved > 0) {
      System.arraycopy(m_documents, index + 1, m_documents, index, numMoved);
    }
    m_documents[--m_size] = null;
    return oldValue;
  }

  /** @see java.util.AbstractList#set(int, java.lang.Object) */
  @Override
  public CmsDocument set(int index, CmsDocument element) {

    rangeCheck(index);
    CmsDocument newValue = element;
    CmsDocument oldValue = m_documents[index];
    m_documents[index] = newValue;
    return oldValue;
  }

  /** @see java.util.AbstractCollection#size() */
  @Override
  public int size() {

    return m_size;
  }

  /**
   * Checks if the document is added as a subdocument or attachment.
   *
   * <p>If the document was not merged, it is a new document that has to be added to the list
   * depending on the only versions flag.
   *
   * <p>
   *
   * @param doc the document to check
   * @return true if the document was added as subdocument, attachment or if the document should not
   *     be added, otherwise false
   */
  private boolean checkContainer(CmsDocument doc) {

    boolean docnameWithoutPostfixEquality;
    for (int i = 0; i < m_size; i++) {
      docnameWithoutPostfixEquality =
          doc.getDocumentNameFullWithoutPostfix()
              .equals(m_documents[i].getDocumentNameFullWithoutPostfix());
      if (m_documents[i].isVersionOf(doc) || (m_useTypes && docnameWithoutPostfixEquality)) {
        if (m_useTypes) {

          if ((m_defaultType != null) && (m_defaultType.equals(doc.getPostfix()))) {
            if (m_documents[i].getTypes().size() > 0) {
              doc.setTypes(m_documents[i].getTypes());
            }
            doc.addType(m_documents[i]);
            m_documents[i] = doc;
          } else {
            m_documents[i].addType(doc);
          }
          return true;
        } else if ((m_useAttachments) && (doc.isAttachment()) && (!m_documents[i].isAttachment())) {
          // store attachment in temporary list to assign later
          m_attachments.add(doc);
          return true;
        } else if ((m_useAttachments) && (m_documents[i].isAttachment()) && (!doc.isAttachment())) {
          if (isOnlyVersions()) {
            return true;
          } else {
            m_attachments.add(m_documents[i]);
            m_documents[i] = doc;
            return true;
          }
        } else if (m_useLanguages) {
          // merge the document if languages are used
          m_documents[i] = m_documents[i].mergeDocuments(doc);
          return true;
        }
      } else if (isOnlyVersions()) {
        // this is no version of a present document, do not add it at all
        return true;
      }
    }
    return false;
  }

  /**
   * Converts an object to a document.
   *
   * <p>
   *
   * @param element the object to convert
   * @return the document object representation
   */
  private CmsDocument convertDocument(Object element) {

    try {
      return (CmsDocument) element;
    } catch (Exception e) {
      if (LOG.isErrorEnabled()) {
        LOG.error("Tried adding wrong object to document list " + e);
      }
    }
    return CmsDocument.getNullDocument();
  }

  /**
   * Returns the flag if only versions of a specific document should be added to the list.
   *
   * <p>
   *
   * @return the flag if only versions of a specific document should be added to the list
   */
  private boolean isOnlyVersions() {

    return m_onlyVersions;
  }

  /**
   * Check if the given index is in range. If not, throw an appropriate runtime exception. This
   * method does *not* check if the index is negative: It is always used immediately prior to an
   * array access, which throws an ArrayIndexOutOfBoundsException if index is negative.
   *
   * @param index the index to check
   */
  private void rangeCheck(int index) {

    if ((index > m_size) || (index < 0)) {
      throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + m_size);
    }
  }
}
/**
 * Describes a scheduled job for the OpenCms scheduler.
 *
 * <p>
 *
 * <p>The time the scheduled job is executed is defined with Unix 'cron-like' definitions.
 *
 * <p>For those unfamiliar with "cron", this means being able to create a firing schedule such as:
 * "At 8:00am every Monday through Friday" or "At 1:30am every last Friday of the month".
 *
 * <p>A "Cron-Expression" is a string comprised of 6 or 7 fields separated by white space. The 6
 * mandatory and 1 optional fields are as follows: <br>
 *
 * <table cellspacing="8">
 * <tr>
 * <th align="left">Field Name</th>
 * <th align="left">&nbsp;</th>
 * <th align="left">Allowed Values</th>
 * <th align="left">&nbsp;</th>
 * <th align="left">Allowed Special Characters</th>
 * </tr>
 * <tr>
 * <td align="left"><code>Seconds</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>0-59</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>, - * /</code></td>
 * </tr>
 * <tr>
 * <td align="left"><code>Minutes</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>0-59</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>, - * /</code></td>
 * </tr>
 * <tr>
 * <td align="left"><code>Hours</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>0-23</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>, - * /</code></td>
 * </tr>
 * <tr>
 * <td align="left"><code>Day-of-month</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>1-31</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>, - * ? / L C</code></td>
 * </tr>
 * <tr>
 * <td align="left"><code>Month</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>1-12 or JAN-DEC</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>, - * /</code></td>
 * </tr>
 * <tr>
 * <td align="left"><code>Day-of-Week</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>1-7 or SUN-SAT</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>, - * ? / L C #</code></td>
 * </tr>
 * <tr>
 * <td align="left"><code>Year (Optional)</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>empty, 1970-2099</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>, - * /</code></td>
 * </tr>
 * </table>
 *
 * <p>The '*' character is used to specify all values. For example, "*" in the minute field means
 * "every minute".
 *
 * <p>The '?' character is allowed for the day-of-month and day-of-week fields. It is used to
 * specify 'no specific value'. This is useful when you need to specify something in one of the two
 * fields, but not the other. See the examples below for clarification.
 *
 * <p>The '-' character is used to specify ranges For example "10-12" in the hour field means "the
 * hours 10, 11 and 12".
 *
 * <p>The ',' character is used to specify additional values. For example "MON,WED,FRI" in the
 * day-of-week field means "the days Monday, Wednesday, and Friday".
 *
 * <p>The '/' character is used to specify increments. For example "0/15" in the seconds field means
 * "the seconds 0, 15, 30, and 45". And "5/15" in the seconds field means "the seconds 5, 20, 35,
 * and 50". You can also specify '/' after the '*' character - in this case '*' is equivalent to
 * having '0' before the '/'.
 *
 * <p>The 'L' character is allowed for the day-of-month and day-of-week fields. This character is
 * short-hand for "last", but it has different meaning in each of the two fields. For example, the
 * value "L" in the day-of-month field means "the last day of the month" - day 31 for January, day
 * 28 for February on non-leap years. If used in the day-of-week field by itself, it simply means
 * "7" or "SAT". But if used in the day-of-week field after another value, it means "the last xxx
 * day of the month" - for example "6L" means "the last friday of the month". When using the 'L'
 * option, it is important not to specify lists, or ranges of values, as you'll get confusing
 * results.
 *
 * <p>The 'W' character is allowed for the day-of-month field. This character is used to specify the
 * weekday (Monday-Friday) nearest the given day. As an example, if you were to specify "15W" as the
 * value for the day-of-month field, the meaning is: "the nearest weekday to the 15th of the month".
 * So if the 15th is a Saturday, the trigger will fire on Friday the 14th. If the 15th is a Sunday,
 * the trigger will fire on Monday the 16th. If the 15th is a Tuesday, then it will fire on Tuesday
 * the 15th. However if you specify "1W" as the value for day-of-month, and the 1st is a Saturday,
 * the trigger will fire on Monday the 3rd, as it will not 'jump' over the boundary of a month's
 * days. The 'W' character can only be specified when the day-of-month is a single day, not a range
 * or list of days.
 *
 * <p>The 'L' and 'W' characters can also be combined for the day-of-month expression to yield 'LW',
 * which translates to "last weekday of the month".
 *
 * <p>The '#' character is allowed for the day-of-week field. This character is used to specify "the
 * nth" XXX day of the month. For example, the value of "6#3" in the day-of-week field means the
 * third Friday of the month (day 6 = Friday and "#3" = the 3rd one in the month). Other examples:
 * "2#1" = the first Monday of the month and "4#5" = the fifth Wednesday of the month. Note that if
 * you specify "#5" and there is not 5 of the given day-of-week in the month, then no firing will
 * occur that month.
 *
 * <p>The 'C' character is allowed for the day-of-month and day-of-week fields. This character is
 * short-hand for "calendar". This means values are calculated against the associated calendar, if
 * any. If no calendar is associated, then it is equivalent to having an all-inclusive calendar. A
 * value of "5C" in the day-of-month field means "the first day included by the calendar on or after
 * the 5th". A value of "1C" in the day-of-week field means "the first day included by the calendar
 * on or after sunday".
 *
 * <p>The legal characters and the names of months and days of the week are not case sensitive.
 *
 * <p>Here are some full examples: <br>
 *
 * <table cellspacing="8">
 * <tr>
 * <th align="left">Expression</th>
 * <th align="left">&nbsp;</th>
 * <th align="left">Meaning</th>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 0 12 * * ?"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 12pm (noon) every day</code></td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 ? * *"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am every day</code></td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 * * ?"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am every day</code></td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 * * ? *"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am every day</code></td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 * * ? 2005"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am every day during the year 2005</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 * 14 * * ?"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire every minute starting at 2pm and ending at 2:59pm, every day</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 0/5 14 * * ?"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 0/5 14,18 * * ?"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 0-5 14 * * ?"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire every minute starting at 2pm and ending at 2:05pm, every day</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 10,44 14 ? 3 WED"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 ? * MON-FRI"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 15 * ?"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am on the 15th day of every month</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 L * ?"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am on the last day of every month</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 ? * 6L"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am on the last Friday of every month</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 ? * 6L"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am on the last Friday of every month</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 ? * 6L 2002-2005"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005</code>
 * </td>
 * </tr>
 * <tr>
 * <td align="left"><code>"0 15 10 ? * 6#3"</code></td>
 * <td align="left">&nbsp;</td>
 * <td align="left"><code>Fire at 10:15am on the third Friday of every month</code>
 * </td>
 * </tr>
 * </table>
 *
 * <p>Pay attention to the effects of '?' and '*' in the day-of-week and day-of-month fields!
 *
 * <p><b>NOTES:</b>
 *
 * <ul>
 *   <li>Support for the features described for the 'C' character is not complete.
 *   <li>Support for specifying both a day-of-week and a day-of-month value is not complete (you'll
 *       need to use the '?' character in on of these fields).
 *   <li>Be careful when setting fire times between mid-night and 1:00 AM - "daylight savings" can
 *       cause a skip or a repeat depending on whether the time moves back or jumps forward.
 * </ul>
 *
 * @since 6.0.0
 */
public class CmsScheduledJobInfo implements I_CmsConfigurationParameterHandler {

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsScheduledJobInfo.class);

  /** Indicates if this job is currently active in the scheduler or not. */
  private boolean m_active;

  /** The name of the class to schedule. */
  private String m_className;

  /** The context information for the user to execute the job with. */
  private CmsContextInfo m_context;

  /** The cron expression for this scheduler job. */
  private String m_cronExpression;

  /** Indicates if the configuration of this job is finalized (frozen). */
  private boolean m_frozen;

  /** The id of this job. */
  private String m_id;

  /** Instance object of the scheduled job (only required when instance is re-used). */
  private I_CmsScheduledJob m_jobInstance;

  /** The name of the job (for information purposes). */
  private String m_jobName;

  /** The parameters used for this job entry. */
  private SortedMap<String, String> m_parameters;

  /** Indicates if the job instance should be re-used if the job is run. */
  private boolean m_reuseInstance;

  /** The (cron) trigger used for scheduling this job. */
  private Trigger m_trigger;

  /**
   * Default constructor.
   *
   * <p>
   */
  public CmsScheduledJobInfo() {

    m_reuseInstance = false;
    m_frozen = false;
    // parameters are stored in a tree map
    m_parameters = new TreeMap<String, String>();
    // a job is active by default
    m_active = true;
  }

  /**
   * Constructor for creating a new job with all required parameters.
   *
   * <p>
   *
   * @param id the id of the job of <code>null</code> if a new id should be automatically generated
   * @param jobName the display name of the job
   * @param className the class name of the job, must be an instance of <code>
   *     {@link I_CmsScheduledJob}</code>
   * @param context the OpenCms user context information to use when executing the job
   * @param cronExpression the cron expression for scheduling the job
   * @param reuseInstance indicates if the job class should be re-used
   * @param active indicates if the job should be active in the scheduler
   * @param parameters the job parameters
   */
  public CmsScheduledJobInfo(
      String id,
      String jobName,
      String className,
      CmsContextInfo context,
      String cronExpression,
      boolean reuseInstance,
      boolean active,
      SortedMap<String, String> parameters) {

    m_frozen = false;
    setId(id);
    if (CmsStringUtil.isNotEmpty(jobName)) {
      // job name is optional, if not present class name will be used
      setJobName(jobName);
    }
    setClassName(className);
    setContextInfo(context);
    setCronExpression(cronExpression);
    setReuseInstance(reuseInstance);
    setActive(active);
    setParameters(parameters);
  }

  /**
   * @see
   *     org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String,
   *     java.lang.String)
   */
  public void addConfigurationParameter(String paramName, String paramValue) {

    checkFrozen();
    // add the configured parameter
    m_parameters.put(paramName, paramValue);
    if (LOG.isDebugEnabled()) {
      LOG.debug(
          org.opencms.configuration.Messages.get()
              .getBundle()
              .key(
                  org.opencms.configuration.Messages.LOG_ADD_CONFIG_PARAM_3,
                  paramName,
                  paramValue,
                  this));
    }
  }

  /**
   * Clears the id of the job.
   *
   * <p>This is useful if you want to create a copy of a job without keeping the job id. Use <code>
   * {@link CmsScheduledJobInfo#clone()}</code> first to create the copy, and then clear the id of
   * the clone.
   *
   * <p>
   */
  public void clearId() {

    setId(null);
  }

  /**
   * Creates a clone of this scheduled job.
   *
   * <p>The clone will not be active in the scheduler until it is scheduled with <code>
   * {@link CmsScheduleManager#scheduleJob(org.opencms.file.CmsObject, CmsScheduledJobInfo)}</code>.
   * The job id returned by <code>{@link #getId()}</code> will be the same. The <code>
   * {@link #isActive()}</code> flag will be set to false. The clones job instance class will be the
   * same if the <code>{@link #isReuseInstance()}</code> flag is set.
   *
   * <p>
   *
   * @see java.lang.Object#clone()
   */
  @Override
  public Object clone() {

    CmsScheduledJobInfo result = new CmsScheduledJobInfo();

    result.m_id = m_id;
    result.m_active = false;
    result.m_frozen = false;
    result.m_className = m_className;
    if (isReuseInstance()) {
      result.m_jobInstance = m_jobInstance;
    }
    result.m_reuseInstance = m_reuseInstance;
    result.m_context = (CmsContextInfo) m_context.clone();
    result.m_cronExpression = m_cronExpression;
    result.m_jobName = m_jobName;
    result.m_parameters = new TreeMap<String, String>(m_parameters);
    result.m_trigger = null;

    return result;
  }

  /**
   * Returns the name of the class to schedule.
   *
   * <p>
   *
   * @return the name of the class to schedule
   */
  public String getClassName() {

    return m_className;
  }

  /** @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration() */
  public CmsParameterConfiguration getConfiguration() {

    // this configuration does not support parameters
    if (LOG.isDebugEnabled()) {
      LOG.debug(
          org.opencms.configuration.Messages.get()
              .getBundle()
              .key(org.opencms.configuration.Messages.LOG_GET_CONFIGURATION_1, this));
    }

    return new CmsParameterConfiguration(getParameters());
  }

  /**
   * Returns the context information for the user executing this job.
   *
   * <p>Please note: The context time returned by {@link
   * org.opencms.file.CmsRequestContext#getRequestTime()} will be set to the time when this job was
   * created. This can be relevant in case you want to perform VFS operations, because it will
   * affect how resources are processed that have date released / date expired attributes set.
   *
   * <p>
   *
   * @return the context information for the user executing this job
   */
  public CmsContextInfo getContextInfo() {

    return m_context;
  }

  /**
   * Returns the cron expression for this job entry.
   *
   * <p>
   *
   * @return the cron expression for this job entry
   */
  public String getCronExpression() {

    return m_cronExpression;
  }

  /**
   * Returns the next time at which this job will be executed, after the given time.
   *
   * <p>If this job will not be executed after the given time, <code>null</code> will be returned..
   *
   * <p>
   *
   * @param date the after which the next execution time should be calculated
   * @return the next time at which this job will be executed, after the given time
   */
  public Date getExecutionTimeAfter(Date date) {

    if (!m_active || (m_trigger == null)) {
      // if the job is not active, no time can be calculated
      return null;
    }

    return m_trigger.getFireTimeAfter(date);
  }

  /**
   * Returns the next time at which this job will be executed.
   *
   * <p>If the job will not execute again, <code>null</code> will be returned.
   *
   * <p>
   *
   * @return the next time at which this job will be executed
   */
  public Date getExecutionTimeNext() {

    if (!m_active || (m_trigger == null)) {
      // if the job is not active, no time can be calculated
      return null;
    }

    return m_trigger.getNextFireTime();
  }

  /**
   * Returns the previous time at which this job will be executed.
   *
   * <p>If this job has not yet been executed, <code>null</code> will be returned.
   *
   * @return the previous time at which this job will be executed
   */
  public Date getExecutionTimePrevious() {

    if (!m_active || (m_trigger == null)) {
      // if the job is not active, no time can be calculated
      return null;
    }

    return m_trigger.getPreviousFireTime();
  }

  /**
   * Returns the internal id of this job in the scheduler.
   *
   * <p>Can be used to remove this job from the scheduler with <code>
   * {@link CmsScheduleManager#unscheduleJob(org.opencms.file.CmsObject, String)}</code>.
   *
   * <p>
   *
   * @return the internal id of this job in the scheduler
   */
  public String getId() {

    return m_id;
  }

  /**
   * Returns an instance of the configured job class.
   *
   * <p>If any error occurs during class invocaion, the error is written to the OpenCms log and
   * <code>null</code> is returned.
   *
   * <p>
   *
   * @return an instance of the configured job class, or null if an error occurred
   */
  public synchronized I_CmsScheduledJob getJobInstance() {

    if (m_jobInstance != null) {

      if (LOG.isDebugEnabled()) {
        LOG.debug(
            Messages.get()
                .getBundle()
                .key(Messages.LOG_REUSING_INSTANCE_1, m_jobInstance.getClass().getName()));
      }

      // job instance already initialized
      return m_jobInstance;
    }

    I_CmsScheduledJob job = null;

    try {
      // create an instance of the OpenCms job class
      job = (I_CmsScheduledJob) Class.forName(getClassName()).newInstance();
    } catch (ClassNotFoundException e) {
      LOG.error(Messages.get().getBundle().key(Messages.LOG_CLASS_NOT_FOUND_1, getClassName()), e);
    } catch (IllegalAccessException e) {
      LOG.error(Messages.get().getBundle().key(Messages.LOG_ILLEGAL_ACCESS_0), e);
    } catch (InstantiationException e) {
      LOG.error(Messages.get().getBundle().key(Messages.LOG_INSTANCE_GENERATION_0), e);
    } catch (ClassCastException e) {
      LOG.error(Messages.get().getBundle().key(Messages.LOG_BAD_INTERFACE_0), e);
    }

    if (m_reuseInstance) {
      // job instance must be re-used
      m_jobInstance = job;
    }

    if (LOG.isDebugEnabled()) {
      LOG.debug(Messages.get().getBundle().key(Messages.LOG_JOB_CREATED_1, getClassName()));
    }

    // this should not flood the log files: if class name is wrong or jar files missing this will
    // most likely persist until restart.
    if (job == null) {
      setActive(false);
    }
    return job;
  }

  /**
   * Returns the job name.
   *
   * <p>
   *
   * @return the job name
   */
  public String getJobName() {

    return m_jobName;
  }

  /**
   * Returns the parameters.
   *
   * <p>
   *
   * @return the parameters
   */
  public SortedMap<String, String> getParameters() {

    return m_parameters;
  }

  /**
   * Finalizes (freezes) the configuration of this scheduler job entry.
   *
   * <p>After this job entry has been frozen, any attempt to change the configuration of this entry
   * with one of the "set..." methods will lead to a <code>RuntimeException</code>.
   *
   * <p>
   *
   * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration()
   */
  public void initConfiguration() {

    // simple default configuration does not need to be initialized
    if (LOG.isDebugEnabled()) {
      LOG.debug(
          org.opencms.configuration.Messages.get()
              .getBundle()
              .key(org.opencms.configuration.Messages.LOG_INIT_CONFIGURATION_1, this));
    }
    setFrozen(true);
  }

  /**
   * Returns <code>true</code> if this job is currently active in the scheduler.
   *
   * <p>
   *
   * @return <code>true</code> if this job is currently active in the scheduler
   */
  public boolean isActive() {

    return m_active;
  }

  /**
   * Returns true if the job instance class is reused for this job.
   *
   * <p>
   *
   * @return true if the job instance class is reused for this job
   */
  public boolean isReuseInstance() {

    return m_reuseInstance;
  }

  /**
   * Sets the active state of this job.
   *
   * <p>
   *
   * @param active the active state to set
   */
  public void setActive(boolean active) {

    checkFrozen();
    m_active = active;
  }

  /**
   * Sets the name of the class to schedule.
   *
   * <p>
   *
   * @param className the class name to set
   */
  public void setClassName(String className) {

    checkFrozen();
    if (!CmsStringUtil.isValidJavaClassName(className)) {
      CmsMessageContainer message =
          Messages.get().container(Messages.ERR_BAD_JOB_CLASS_NAME_1, className);
      if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_2_INITIALIZING) {
        throw new CmsIllegalArgumentException(message);
      } else {
        LOG.warn(message.key());
      }
    } else {
      Class<?> jobClass;
      try {
        jobClass = Class.forName(className);
        if (!I_CmsScheduledJob.class.isAssignableFrom(jobClass)) {
          CmsMessageContainer message =
              Messages.get()
                  .container(
                      Messages.ERR_JOB_CLASS_BAD_INTERFACE_2,
                      className,
                      I_CmsScheduledJob.class.getName());

          if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_2_INITIALIZING) {
            throw new CmsIllegalArgumentException(message);
          } else {
            LOG.warn(message.key());
          }
        }
      } catch (ClassNotFoundException e) {
        CmsMessageContainer message =
            Messages.get().container(Messages.ERR_JOB_CLASS_NOT_FOUND_1, className);
        if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_2_INITIALIZING) {
          throw new CmsIllegalArgumentException(message);
        } else {
          LOG.warn(message.key());
        }
      }
    }
    m_className = className;
    if (getJobName() == null) {
      // initialize job name with class name as default
      setJobName(className);
    }
  }

  /**
   * Sets the context information for the user executing the job.
   *
   * <p>This will also "freeze" the context information that is set.
   *
   * <p>
   *
   * @param contextInfo the context information for the user executing the job
   * @see CmsContextInfo#freeze()
   */
  public void setContextInfo(CmsContextInfo contextInfo) {

    checkFrozen();
    if (contextInfo == null) {
      throw new CmsIllegalArgumentException(
          Messages.get().container(Messages.ERR_BAD_CONTEXT_INFO_0));
    }
    m_context = contextInfo;
  }

  /**
   * Sets the cron expression for this job entry.
   *
   * <p>
   *
   * @param cronExpression the cron expression to set
   */
  public void setCronExpression(String cronExpression) {

    checkFrozen();

    try {
      // check if the cron expression is valid
      new CronTrigger().setCronExpression(cronExpression);
    } catch (Exception e) {
      throw new CmsIllegalArgumentException(
          Messages.get()
              .container(Messages.ERR_BAD_CRON_EXPRESSION_2, getJobName(), cronExpression));
    }

    m_cronExpression = cronExpression;
  }

  /**
   * Sets the job name.
   *
   * <p>
   *
   * @param jobName the job name to set
   */
  public void setJobName(String jobName) {

    checkFrozen();
    if (CmsStringUtil.isEmpty(jobName) || !jobName.trim().equals(jobName)) {
      throw new CmsIllegalArgumentException(
          Messages.get().container(Messages.ERR_BAD_JOB_NAME_1, jobName));
    }
    m_jobName = jobName;
  }

  /**
   * Sets the job parameters.
   *
   * <p>
   *
   * @param parameters the parameters to set
   */
  public void setParameters(SortedMap<String, String> parameters) {

    checkFrozen();
    if (parameters == null) {
      throw new CmsIllegalArgumentException(
          Messages.get().container(Messages.ERR_BAD_JOB_PARAMS_0));
    }
    // make sure the parameters are a sorted map
    m_parameters = new TreeMap<String, String>(parameters);
  }

  /**
   * Controls if the job instance class is reused for this job, of if a new instance is generated
   * every time the job is run.
   *
   * <p>
   *
   * @param reuseInstance must be true if the job instance class is to be reused
   */
  public void setReuseInstance(boolean reuseInstance) {

    checkFrozen();
    m_reuseInstance = reuseInstance;
  }

  /**
   * Checks if this job info configuration is frozen.
   *
   * <p>
   *
   * @throws CmsRuntimeException in case the configuration is already frozen
   */
  protected void checkFrozen() throws CmsRuntimeException {

    if (m_frozen) {
      throw new CmsRuntimeException(
          Messages.get().container(Messages.ERR_JOB_INFO_FROZEN_1, getJobName()));
    }
  }

  /**
   * Returns the Quartz trigger used for scheduling this job.
   *
   * <p>This is an internal operation that should only by performed by the <code>
   * {@link CmsScheduleManager}</code>, never by using this API directly.
   *
   * <p>
   *
   * @return the Quartz trigger used for scheduling this job
   */
  protected Trigger getTrigger() {

    return m_trigger;
  }

  /**
   * Sets the "frozen" state of this job.
   *
   * <p>This is an internal operation to be used only by the <code>{@link CmsScheduleManager}</code>
   * .
   *
   * <p>
   *
   * @param frozen the "frozen" state to set
   */
  protected synchronized void setFrozen(boolean frozen) {

    if (frozen && !m_frozen) {
      // "freeze" the job configuration
      m_parameters = Collections.unmodifiableSortedMap(m_parameters);
      m_context.freeze();
      m_frozen = true;
    } else if (!frozen && m_frozen) {
      // "unfreeze" the job configuration
      m_parameters = new TreeMap<String, String>(m_parameters);
      m_frozen = false;
    }
  }

  /**
   * Sets the is used for scheduling this job.
   *
   * <p>This is an internal operation that should only by performed by the <code>
   * {@link CmsScheduleManager}</code>, never by using this API directly.
   *
   * <p>
   *
   * @param id the id to set
   */
  protected void setId(String id) {

    checkFrozen();
    m_id = id;
  }

  /**
   * Sets the Quartz trigger used for scheduling this job.
   *
   * <p>This is an internal operation that should only by performed by the <code>
   * {@link CmsScheduleManager}</code>, never by using this API directly.
   *
   * <p>
   *
   * @param trigger the Quartz trigger to set
   */
  protected void setTrigger(Trigger trigger) {

    checkFrozen();
    m_trigger = trigger;
  }

  /**
   * Updates the request time in the internal context information of the user with the current
   * system time.
   *
   * <p>This is required before executing the job, otherwise the context information request time
   * would be the time the context object was initially created.
   *
   * <p>
   */
  protected void updateContextRequestTime() {

    CmsContextInfo context = (CmsContextInfo) m_context.clone();
    context.setRequestTime(System.currentTimeMillis());
    context.freeze();
    m_context = context;
  }
}
/**
 * Contains the contents of a cached resource.
 *
 * <p>It is basically a list of pre-generated output, include() calls to other resources (with
 * request parameters) and http headers that this resource requires to be set.
 *
 * <p>A CmsFlexCacheEntry might also describe a redirect-call, but in this case nothing else will be
 * cached.
 *
 * <p>The pre-generated output is saved in <code>byte[]</code> arrays. The include() calls are saved
 * as Strings of the included resource name, the parameters for the calls are saved in a HashMap.
 * The headers are saved in a HashMap. In case of a redirect, the redirect target is cached in a
 * String.
 *
 * <p>The CmsFlexCacheEntry can also have an expire date value, which indicates the time that his
 * entry will become invalid and should thus be cleared from the cache.
 *
 * <p>
 *
 * @since 6.0.0
 * @see org.opencms.cache.I_CmsLruCacheObject
 */
public class CmsFlexCacheEntry implements I_CmsLruCacheObject, I_CmsMemoryMonitorable {

  /** Initial size for lists. */
  public static final int INITIAL_CAPACITY_LISTS = 10;

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsFlexCacheEntry.class);

  /** The CacheEntry's size in bytes. */
  private int m_byteSize;

  /** Indicates if this cache entry is completed. */
  private boolean m_completed;

  /** The "expires" date for this Flex cache entry. */
  private long m_dateExpires;

  /** The "last modified" date for this Flex cache entry. */
  private long m_dateLastModified;

  /** The list of items for this resource. */
  private List<Object> m_elements;

  /** A Map of cached headers for this resource. */
  private Map<String, List<String>> m_headers;

  /** Pointer to the next cache entry in the LRU cache. */
  private I_CmsLruCacheObject m_next;

  /** Pointer to the previous cache entry in the LRU cache. */
  private I_CmsLruCacheObject m_previous;

  /** A redirection target (if redirection is set). */
  private String m_redirectTarget;

  /** The key under which this cache entry is stored in the variation map. */
  private String m_variationKey;

  /** The variation map where this cache entry is stored. */
  private Map<String, I_CmsLruCacheObject> m_variationMap;

  /**
   * Constructor for class CmsFlexCacheEntry.
   *
   * <p>The way to use this class is to first use this empty constructor and later add data with the
   * various add methods.
   */
  public CmsFlexCacheEntry() {

    m_elements = new ArrayList<Object>(INITIAL_CAPACITY_LISTS);
    m_dateExpires = CmsResource.DATE_EXPIRED_DEFAULT;
    m_dateLastModified = -1;
    // base memory footprint of this object with all referenced objects
    m_byteSize = 1024;

    setNextLruObject(null);
    setPreviousLruObject(null);
  }

  /**
   * Adds an array of bytes to this cache entry, this will usually be the result of some kind of
   * output - stream.
   *
   * <p>
   *
   * @param bytes the output to save in the cache
   */
  public void add(byte[] bytes) {

    if (m_completed) {
      return;
    }
    if (m_redirectTarget == null) {
      // Add only if not already redirected
      m_elements.add(bytes);
      m_byteSize += CmsMemoryMonitor.getMemorySize(bytes);
    }
  }

  /**
   * Add an include - call target resource to this cache entry.
   *
   * <p>
   *
   * @param resource a name of a resource in the OpenCms VFS
   * @param parameters a map of parameters specific to this include call
   * @param attrs a map of request attributes specific to this include call
   */
  public void add(String resource, Map<String, String[]> parameters, Map<String, Object> attrs) {

    if (m_completed) {
      return;
    }
    if (m_redirectTarget == null) {
      // Add only if not already redirected
      m_elements.add(resource);
      m_byteSize += CmsMemoryMonitor.getMemorySize(resource);
      if (parameters == null) {
        parameters = Collections.emptyMap();
      }
      m_elements.add(parameters);
      m_byteSize += CmsMemoryMonitor.getValueSize(parameters);
      if (attrs == null) {
        attrs = Collections.emptyMap();
      }
      m_elements.add(attrs);
      m_byteSize += CmsMemoryMonitor.getValueSize(attrs);
    }
  }

  /**
   * Add a map of headers to this cache entry, which are usually collected in the class
   * CmsFlexResponse first.
   *
   * <p>
   *
   * @param headers the map of headers to add to the entry
   */
  public void addHeaders(Map<String, List<String>> headers) {

    if (m_completed) {
      return;
    }
    m_headers = headers;

    Iterator<String> allHeaders = m_headers.keySet().iterator();
    while (allHeaders.hasNext()) {
      m_byteSize += CmsMemoryMonitor.getMemorySize(allHeaders.next());
    }
  }

  /** @see org.opencms.cache.I_CmsLruCacheObject#addToLruCache() */
  public void addToLruCache() {

    // do nothing here...
    if (LOG.isDebugEnabled()) {
      LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXCACHEENTRY_ADDED_ENTRY_1, this));
    }
  }

  /**
   * Completes this cache entry.
   *
   * <p>A completed cache entry is made "unmodifiable", so that no further data can be added and
   * existing data can not be changed.
   *
   * <p>This is to prevent the (unlikely) case that some user-written class tries to make changes to
   * a cache entry.
   *
   * <p>
   */
  public void complete() {

    m_completed = true;
    // Prevent changing of the cached lists
    if (m_headers != null) {
      m_headers = Collections.unmodifiableMap(m_headers);
    }
    if (m_elements != null) {
      m_elements = Collections.unmodifiableList(m_elements);
    }
    if (LOG.isDebugEnabled()) {
      LOG.debug(
          Messages.get()
              .getBundle()
              .key(Messages.LOG_FLEXCACHEENTRY_ENTRY_COMPLETED_1, toString()));
    }
  }

  /**
   * Returns the list of data entries of this cache entry.
   *
   * <p>Data entries are byte arrays representing some kind of output or Strings representing
   * include calls to other resources.
   *
   * <p>
   *
   * @return the list of data elements of this cache entry
   */
  public List<Object> elements() {

    return m_elements;
  }

  /**
   * Returns the expiration date of this cache entry, this is set to the time when the entry becomes
   * invalid.
   *
   * <p>
   *
   * @return the expiration date value for this resource
   */
  public long getDateExpires() {

    return m_dateExpires;
  }

  /**
   * Returns the "last modified" date for this Flex cache entry.
   *
   * <p>
   *
   * @return the "last modified" date for this Flex cache entry
   */
  public long getDateLastModified() {

    return m_dateLastModified;
  }

  /** @see org.opencms.cache.I_CmsLruCacheObject#getLruCacheCosts() */
  public int getLruCacheCosts() {

    return m_byteSize;
  }

  /** @see org.opencms.monitor.I_CmsMemoryMonitorable#getMemorySize() */
  public int getMemorySize() {

    return getLruCacheCosts();
  }

  /** @see org.opencms.cache.I_CmsLruCacheObject#getNextLruObject() */
  public I_CmsLruCacheObject getNextLruObject() {

    return m_next;
  }

  /** @see org.opencms.cache.I_CmsLruCacheObject#getPreviousLruObject() */
  public I_CmsLruCacheObject getPreviousLruObject() {

    return m_previous;
  }

  /** @see org.opencms.cache.I_CmsLruCacheObject#getValue() */
  public Object getValue() {

    return m_elements;
  }

  /** @see org.opencms.cache.I_CmsLruCacheObject#removeFromLruCache() */
  public void removeFromLruCache() {

    if ((m_variationMap != null) && (m_variationKey != null)) {
      m_variationMap.remove(m_variationKey);
    }
    if (LOG.isDebugEnabled()) {
      LOG.debug(
          Messages.get()
              .getBundle()
              .key(Messages.LOG_FLEXCACHEENTRY_REMOVED_ENTRY_FOR_VARIATION_1, m_variationKey));
    }
  }

  /**
   * Processing method for this cached entry.
   *
   * <p>If this method is called, it delivers the contents of the cached entry to the given request
   * / response. This includes calls to all included resources.
   *
   * <p>
   *
   * @param req the request from the client
   * @param res the server response
   * @throws CmsFlexCacheException is thrown when problems writing to the response output-stream
   *     occur
   * @throws ServletException might be thrown from call to RequestDispatcher.include()
   * @throws IOException might be thrown from call to RequestDispatcher.include() or from
   *     Response.sendRedirect()
   */
  public void service(CmsFlexRequest req, CmsFlexResponse res)
      throws CmsFlexCacheException, ServletException, IOException {

    if (!m_completed) {
      return;
    }

    if (m_redirectTarget != null) {
      res.setOnlyBuffering(false);
      // redirect the response, no further output required
      res.sendRedirect(m_redirectTarget);
    } else {
      // process cached headers first
      CmsFlexResponse.processHeaders(m_headers, res);
      // check if this cache entry is a "leaf" (i.e. no further includes)
      boolean hasNoSubElements = (m_elements.size() == 1);
      // write output to stream and process all included elements
      for (int i = 0; i < m_elements.size(); i++) {
        Object o = m_elements.get(i);
        if (o instanceof String) {
          // handle cached parameters
          i++;
          Map<String, String[]> paramMap = CmsCollectionsGenericWrapper.map(m_elements.get(i));
          Map<String, String[]> oldParamMap = null;
          if (paramMap.size() > 0) {
            oldParamMap = req.getParameterMap();
            req.addParameterMap(paramMap);
          }
          // handle cached attributes
          i++;
          Map<String, Object> attrMap = CmsCollectionsGenericWrapper.map(m_elements.get(i));
          Map<String, Object> oldAttrMap = null;
          if (attrMap.size() > 0) {
            oldAttrMap = req.getAttributeMap();
            req.addAttributeMap(attrMap);
          }
          // do the include call
          req.getRequestDispatcher((String) o).include(req, res);
          // reset parameters if necessary
          if (oldParamMap != null) {
            req.setParameterMap(oldParamMap);
          }
          // reset attributes if necessary
          if (oldAttrMap != null) {
            req.setAttributeMap(oldAttrMap);
          }
        } else {
          try {
            res.writeToOutputStream((byte[]) o, hasNoSubElements);
          } catch (IOException e) {
            CmsMessageContainer message =
                Messages.get()
                    .container(Messages.LOG_FLEXCACHEKEY_NOT_FOUND_1, getClass().getName());
            if (LOG.isDebugEnabled()) {
              LOG.debug(message.key());
            }

            throw new CmsFlexCacheException(message, e);
          }
        }
      }
    }
  }

  /**
   * Sets the expiration date of this Flex cache entry exactly to the given time.
   *
   * <p>
   *
   * @param dateExpires the time to expire this cache entry
   */
  public void setDateExpires(long dateExpires) {

    m_dateExpires = dateExpires;
    if (LOG.isDebugEnabled()) {
      long now = System.currentTimeMillis();
      LOG.debug(
          Messages.get()
              .getBundle()
              .key(
                  Messages.LOG_FLEXCACHEENTRY_SET_EXPIRATION_DATE_3,
                  new Long(m_dateExpires),
                  new Long(now),
                  new Long(m_dateExpires - now)));
    }
  }

  /**
   * Sets an expiration date for this cache entry to the next timeout, which indicates the time this
   * entry becomes invalid.
   *
   * <p>The timeout parameter represents the minute - interval in which the cache entry is to be
   * cleared. The interval always starts at 0.00h. A value of 60 would indicate that this entry will
   * reach it's expiration date at the beginning of the next full hour, a timeout of 20 would
   * indicate that the entry is invalidated at x.00, x.20 and x.40 of every hour etc.
   *
   * <p>
   *
   * @param timeout the timeout value to be set
   */
  public void setDateExpiresToNextTimeout(long timeout) {

    if ((timeout < 0) || !m_completed) {
      return;
    }

    long now = System.currentTimeMillis();
    long daytime = now % 86400000;
    long timeoutMinutes = timeout * 60000;
    setDateExpires(now - (daytime % timeoutMinutes) + timeoutMinutes);
  }

  /**
   * Sets the "last modified" date for this Flex cache entry with the given value.
   *
   * <p>
   *
   * @param dateLastModified the value to set for the "last modified" date
   */
  public void setDateLastModified(long dateLastModified) {

    m_dateLastModified = dateLastModified;
  }

  /**
   * Sets the "last modified" date for this Flex cache entry by using the last passed timeout value.
   *
   * <p>If a cache entry uses the timeout feature, it becomes invalid every time the timeout
   * interval passes. Thus the "last modified" date is the time the last timeout passed.
   *
   * <p>
   *
   * @param timeout the timeout value to use to calculate the date last modified
   */
  public void setDateLastModifiedToPreviousTimeout(long timeout) {

    long now = System.currentTimeMillis();
    long daytime = now % 86400000;
    long timeoutMinutes = timeout * 60000;
    setDateLastModified(now - (daytime % timeoutMinutes));
  }

  /**
   * @see
   *     org.opencms.cache.I_CmsLruCacheObject#setNextLruObject(org.opencms.cache.I_CmsLruCacheObject)
   */
  public void setNextLruObject(I_CmsLruCacheObject theNextEntry) {

    m_next = theNextEntry;
  }

  /**
   * @see
   *     org.opencms.cache.I_CmsLruCacheObject#setPreviousLruObject(org.opencms.cache.I_CmsLruCacheObject)
   */
  public void setPreviousLruObject(I_CmsLruCacheObject thePreviousEntry) {

    m_previous = thePreviousEntry;
  }

  /**
   * Set a redirect target for this cache entry.
   *
   * <p><b>Important:</b> When a redirect target is set, all saved data is thrown away, and new data
   * will not be saved in the cache entry. This is so since with a redirect nothing will be
   * displayed in the browser anyway, so there is no point in saving the data.
   *
   * <p>
   *
   * @param target The redirect target (must be a valid URL).
   */
  public void setRedirect(String target) {

    if (m_completed || (target == null)) {
      return;
    }
    m_redirectTarget = target;
    m_byteSize = 512 + CmsMemoryMonitor.getMemorySize(target);
    // If we have a redirect we don't need any other output or headers
    m_elements = null;
    m_headers = null;
  }

  /**
   * Stores a backward reference to the map and key where this cache entry is stored.
   *
   * <p>This is required for the FlexCache.
   *
   * <p>
   *
   * @param theVariationKey the variation key
   * @param theVariationMap the variation map
   */
  public void setVariationData(
      String theVariationKey, Map<String, I_CmsLruCacheObject> theVariationMap) {

    m_variationKey = theVariationKey;
    m_variationMap = theVariationMap;
  }

  /**
   * @see java.lang.Object#toString()
   * @return a basic String representation of this CmsFlexCache entry
   */
  @Override
  public String toString() {

    String str = null;
    if (m_redirectTarget == null) {
      str =
          "CmsFlexCacheEntry ["
              + m_elements.size()
              + " Elements/"
              + getLruCacheCosts()
              + " bytes]\n";
      Iterator<Object> i = m_elements.iterator();
      int count = 0;
      while (i.hasNext()) {
        count++;
        Object o = i.next();
        if (o instanceof String) {
          str += "" + count + " - <cms:include target=" + o + ">\n";
        } else if (o instanceof byte[]) {
          str += "" + count + " - <![CDATA[" + new String((byte[]) o) + "]]>\n";
        } else {
          str += "<!--[" + o.toString() + "]-->";
        }
      }
    } else {
      str = "CmsFlexCacheEntry [Redirect to target=" + m_redirectTarget + "]";
    }
    return str;
  }
}
Exemple #12
0
/**
 * example: &lt;%@ taglib &nbsp; prefix="lh-dt" &nbsp
 * uri="http://www.langhua.org/taglib/datetime"%&gt; &lt;lh-dt:time /&gt;
 *
 * <p>Image name: Time-display.PNG
 */
public class Time extends A_LanghuaTag {
  private static final long serialVersionUID = 1L;
  private static final Log LOG = CmsLog.getLog(Time.class);

  public int doStartTag() throws JspTagException {

    String htmlbody = buildHtml();

    try {
      pageContext.getOut().print(htmlbody);
    } catch (Exception e) {
      LOG.debug(e);
    }

    return SKIP_BODY;
  }

  public String buildHtml() {
    HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
    HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
    CmsJspActionElement cms = new CmsJspActionElement(pageContext, request, response);
    StringBuffer html = new StringBuffer(512);
    String fileName = null;
    boolean showWeekday = true;
    try {
      if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getCssFile())) {
        String cssfile = cms.getRequestContext().removeSiteRoot(getCssFile());
        CmsObject cmso = cms.getCmsObject();
        Locale locale = cms.getRequestContext().getLocale();
        CmsFile configFile = cmso.readFile(cssfile, CmsResourceFilter.IGNORE_EXPIRATION);
        CmsXmlContent configuration = CmsXmlContentFactory.unmarshal(cmso, configFile);
        showWeekday =
            Boolean.parseBoolean(configuration.getStringValue(cmso, "showweekday", locale));
        fileName = configFile.getName();
        html.append("<style type=\"text/css\">\n");
        html.append("<!--");
        html.append(buildCSS(cms, cssfile));
        html.append("-->");
        html.append("</style>\n");
      }
      html.append(
          "<div"
              + (CmsStringUtil.isEmpty(fileName) ? "" : (" class=\"topTime" + fileName + "\""))
              + ">\n");
      html.append("<SCRIPT language=\"JavaScript\">\n");
      html.append("dayObj=new Date();\n");
      html.append("monthStr=dayObj.getMonth()+1;\n");
      html.append("year=dayObj.getFullYear();\n");
      html.append(
          "document.write(year+\""
              + cms.label(Messages.YEAR)
              + "\"+monthStr+\""
              + cms.label(Messages.MONTH)
              + "\"+dayObj.getDate()+\""
              + cms.label(Messages.DAY)
              + "\"+\" \"); \n");
      if (showWeekday) {
        html.append("document.write(\"&nbsp;\");\n");
        html.append(
            "if(dayObj.getDay()==1) document.write(\"" + cms.label(Messages.XQYI) + "\");\n");
        html.append(
            "if(dayObj.getDay()==2) document.write(\"" + cms.label(Messages.XQER) + "\");\n");
        html.append(
            "if(dayObj.getDay()==3) document.write(\"" + cms.label(Messages.XQSAN) + "\");\n");
        html.append(
            "if(dayObj.getDay()==4) document.write(\"" + cms.label(Messages.XQSI) + "\");\n");
        html.append(
            "if(dayObj.getDay()==5) document.write(\"" + cms.label(Messages.XQWU) + "\");\n");
        html.append(
            "if(dayObj.getDay()==6) document.write(\"" + cms.label(Messages.XQLIU) + "\");\n");
        html.append(
            "if(dayObj.getDay()==0) document.write(\"" + cms.label(Messages.XQRI) + "\");\n");
      }
      html.append("</SCRIPT>");
      html.append("</div>\n");
    } catch (Exception e) {
      LOG.debug(e);
    }

    return html.toString();
  }
}
public class ThesysTrackingHandler extends ThesysLaphoneHandler {
  protected static final Log LOG = CmsLog.getLog(ThesysTrackingHandler.class);

  public ThesysTrackingHandler() {}

  public ThesysTrackingHandler(PageContext context, HttpServletRequest req, HttpServletResponse res)
      throws Exception {
    super(context, req, res);
  }

  public List<ThesysTrackingVO> getPageList() {
    List<ThesysTrackingVO> result = new ArrayList<ThesysTrackingVO>();
    try {
      result =
          ThesysTrackingDAO.getInstance()
              .listByPage(getSiteId(), getMemberId(), getPageSize(), getPageIndex());
    } catch (Exception ex) {
      LOG.error(ex, ex.fillInStackTrace());
    }
    return result;
  }

  public int getCount() {
    int result = 0;
    try {
      result = ThesysTrackingDAO.getInstance().count(getSiteId(), getMemberId());
    } catch (Exception ex) {
      LOG.error(ex, ex.fillInStackTrace());
    }
    return result;
  }

  public boolean add(String itemId) {
    try {
      ThesysTrackingVO vo = ThesysTrackingVO.getInstance(getSiteId(), getMemberId(), itemId);
      ThesysTrackingDAO.getInstance().insert(vo);
    } catch (Exception ex) {
      LOG.error(ex, ex.fillInStackTrace());
      return false;
    }
    return true;
  }

  public boolean delete(String itemId) {
    try {
      ThesysTrackingDAO.getInstance().delete(getSiteId(), getMemberId(), itemId);
    } catch (Exception ex) {
      LOG.error(ex, ex.fillInStackTrace());
      return false;
    }
    return true;
  }

  public boolean deleteAll() {
    try {
      ThesysTrackingDAO.getInstance().deleteAll(getSiteId(), getMemberId());
    } catch (Exception ex) {
      LOG.error(ex, ex.fillInStackTrace());
      return false;
    }
    return true;
  }
}
/**
 * The base class to build a dialog capable of multiple file operations.
 *
 * <p>Extend this class for workplace dialogs that can perform operations on more than one VFS
 * resource like copy, move, touch etc.
 *
 * <p>Provides methods to determine if a multi-resource operation has to be done and helper methods,
 * e.g. to get the list of resources to work with.
 *
 * <p>
 *
 * @since 6.2.0
 */
public abstract class CmsMultiDialog extends CmsDialog {

  /** The delimiter that is used in the resource list request parameter. */
  public static final String DELIMITER_RESOURCES = "|";

  /** Request parameter name for the resource list. */
  public static final String PARAM_RESOURCELIST = "resourcelist";

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsMultiDialog.class);

  /** Collects all eventually thrown exceptions during a multi operation. */
  private CmsMultiException m_multiOperationException;

  /** The resource list parameter value. */
  private String m_paramResourcelist;

  /** The list of resource names for the multi operation. */
  private List<String> m_resourceList;

  /**
   * Public constructor.
   *
   * <p>
   *
   * @param jsp an initialized JSP action element
   */
  public CmsMultiDialog(CmsJspActionElement jsp) {

    super(jsp);
    m_multiOperationException = new CmsMultiException();
  }

  /**
   * Public constructor with JSP variables.
   *
   * <p>
   *
   * @param context the JSP page context
   * @param req the JSP request
   * @param res the JSP response
   */
  public CmsMultiDialog(PageContext context, HttpServletRequest req, HttpServletResponse res) {

    this(new CmsJspActionElement(context, req, res));
  }

  /**
   * Adds an exception thrown during a multi resource operation to the multi exception.
   *
   * <p>After iterating the dialog resources, use {@link
   * #checkMultiOperationException(I_CmsMessageBundle, String)} to display the multi exception
   * depending on collected exceptions.
   *
   * <p>
   *
   * @param exc the exception that was thrown
   */
  public void addMultiOperationException(CmsException exc) {

    m_multiOperationException.addException(exc);
  }

  /**
   * Builds the HTML for the resource list that is affected by the multi operation.
   *
   * <p>
   *
   * @return the HTML for the resource list that is affected by the multi operation
   */
  public String buildResourceList() {

    // check how many resources are selected to decide using a div or not
    boolean scroll = (getResourceList().size() > 6);

    StringBuffer result = new StringBuffer(1024);

    result.append(dialogWhiteBoxStart());

    // if the output to long, wrap it in a div
    if (scroll) {
      result.append("<div style='width: 100%; height:100px; overflow: auto;'>\n");
    }

    result.append("<table border=\"0\">\n");
    Iterator<String> i = getResourceList().iterator();
    while (i.hasNext()) {
      String resName = i.next();
      result.append("\t<tr>\n");
      result.append("\t\t<td class='textbold' style=\"vertical-align:top;\">");
      result.append(CmsResource.getName(resName));
      result.append("&nbsp;</td>\n\t\t<td style=\"vertical-align:top;\">");
      String title = null;
      try {
        // get the title property value
        title =
            getCms()
                .readPropertyObject(resName, CmsPropertyDefinition.PROPERTY_TITLE, false)
                .getValue(null);
      } catch (CmsException e) {
        // ignore this exception, title not found
      }
      if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(title)) {
        // show the title information
        result.append(title);
      }
      result.append("</td>\n\t</tr>\n");
    }
    result.append("</table>");

    // close the div if needed
    if (scroll) {
      result.append("</div>\n");
    }
    result.append(dialogWhiteBoxEnd());
    return result.toString();
  }

  /** @see org.opencms.workplace.CmsDialog#buildLockHeaderBox() */
  @Override
  public String buildLockHeaderBox() throws CmsException {

    if (!isMultiOperation()) {
      return super.buildLockHeaderBox();
    }
    StringBuffer html = new StringBuffer(1024);
    // include multi resource list
    html.append(
        dialogBlockStart(
            key(org.opencms.workplace.commons.Messages.GUI_MULTI_RESOURCELIST_TITLE_0)));
    html.append(buildResourceList());
    html.append(dialogBlockEnd());
    return html.toString();
  }

  /**
   * Checks if an exception occurred during a multi resource operation, and throws a new exception
   * if necessary.
   *
   * <p>
   *
   * @param messages the message bundle to use for the exception message generation
   * @param key the key for the exception to throw with one parameter
   * @throws CmsException the exception that is thrown when the multi operation was not successful
   */
  public void checkMultiOperationException(I_CmsMessageBundle messages, String key)
      throws CmsException {

    if (m_multiOperationException.hasExceptions()) {
      m_multiOperationException.setMessage(
          new CmsMessageContainer(messages, key, new Object[] {m_multiOperationException}));
      throw m_multiOperationException;
    }
  }

  /**
   * Returns the value of the resource list parameter, or null if the parameter is not provided.
   *
   * <p>This parameter selects the resources to perform operations on.
   *
   * <p>
   *
   * @return the value of the resource list parameter or null, if the parameter is not provided
   */
  public String getParamResourcelist() {

    if (CmsStringUtil.isNotEmpty(m_paramResourcelist) && !"null".equals(m_paramResourcelist)) {
      return m_paramResourcelist;
    } else {
      return null;
    }
  }

  /**
   * Returns the resources that are defined for the dialog operation.
   *
   * <p>For single resource operations, the list contains one item: the resource name found in the
   * request parameter value of the "resource" parameter.
   *
   * <p>
   *
   * @return the resources that are defined for the dialog operation
   */
  public List<String> getResourceList() {

    if (m_resourceList == null) {
      // use lazy initializing
      if (getParamResourcelist() != null) {
        // found the resourcelist parameter
        m_resourceList =
            CmsStringUtil.splitAsList(getParamResourcelist(), DELIMITER_RESOURCES, true);
        Collections.sort(m_resourceList);
      } else {
        // this is a single resource operation, create list containing the resource name
        m_resourceList = new ArrayList<String>(1);
        m_resourceList.add(getParamResource());
      }
    }
    return m_resourceList;
  }

  /**
   * Returns the value of the resourcelist parameter in form of a String separated with {@link
   * #DELIMITER_RESOURCES}, or the value of the resource parameter if the first parameter is not
   * provided (no multiple choice has been done.
   *
   * <p>This may be used for jsps as value for the parameter for resources {@link
   * #PARAM_RESOURCELIST}.
   *
   * <p>
   *
   * @return the value of the resourcelist parameter or null, if the parameter is not provided
   */
  public String getResourceListAsParam() {

    String result = getParamResourcelist();
    if (CmsStringUtil.isEmptyOrWhitespaceOnly(result)) {
      result = getParamResource();
    }
    return result;
  }

  /**
   * Returns true if the dialog operation has to be performed on multiple resources.
   *
   * <p>
   *
   * @return true if the dialog operation has to be performed on multiple resources, otherwise false
   */
  public boolean isMultiOperation() {

    return (getResourceList().size() > 1);
  }

  /**
   * Sets the title of the dialog depending on the operation type, multiple or single operation.
   *
   * <p>
   *
   * @param singleKey the key for the single operation
   * @param multiKey the key for the multiple operation
   */
  public void setDialogTitle(String singleKey, String multiKey) {

    if (isMultiOperation()) {
      // generate title with number of selected resources and parent folder parameters
      String resCount = String.valueOf(getResourceList().size());
      String currentFolder = CmsResource.getFolderPath(getSettings().getExplorerResource());
      currentFolder = CmsStringUtil.formatResourceName(currentFolder, 40);
      Object[] params = new Object[] {resCount, currentFolder};
      setParamTitle(key(multiKey, params));
    } else {
      // generate title using the resource name as parameter for the key
      String resourceName = CmsStringUtil.formatResourceName(getParamResource(), 50);
      setParamTitle(key(singleKey, new Object[] {resourceName}));
    }
  }

  /**
   * Sets the value of the resourcelist parameter.
   *
   * <p>
   *
   * @param paramResourcelist the value of the resourcelist parameter
   */
  public void setParamResourcelist(String paramResourcelist) {

    m_paramResourcelist = paramResourcelist;
    m_resourceList = null;
  }

  /** @see org.opencms.workplace.CmsDialog#setParamResource(java.lang.String) */
  @Override
  public void setParamResource(String value) {

    super.setParamResource(value);
    m_resourceList = null;
  }

  /**
   * Checks if the permissions of the current user on the single resource to use in the dialog are
   * sufficient.
   *
   * <p>For a multi resource operation, this returns always true, checks only for single resource
   * operations.
   *
   * <p>
   *
   * @see CmsDialog#checkResourcePermissions(CmsPermissionSet, boolean)
   * @param required the required permissions for the dialog
   * @param neededForFolder if true, the permissions are required for the parent folder of the
   *     resource (e.g. for editors)
   * @return true if the permissions are sufficient, otherwise false
   */
  @Override
  protected boolean checkResourcePermissions(CmsPermissionSet required, boolean neededForFolder) {

    if (isMultiOperation()) {
      // for multi resource operation, return always true
      return true;
    } else {
      // check for single resource operation
      return super.checkResourcePermissions(required, neededForFolder);
    }
  }

  /**
   * Checks if the resource operation is an operation on at least one folder.
   *
   * <p>
   *
   * @return true if the operation an operation on at least one folder, otherwise false
   */
  protected boolean isOperationOnFolder() {

    Iterator<String> i = getResourceList().iterator();
    while (i.hasNext()) {
      String resName = i.next();
      try {
        CmsResource curRes = getCms().readResource(resName, CmsResourceFilter.ALL);
        if (curRes.isFolder()) {
          // found a folder
          return true;
        }
      } catch (CmsException e) {
        // can usually be ignored
        if (LOG.isInfoEnabled()) {
          LOG.info(e.getLocalizedMessage());
        }
      }
    }
    return false;
  }

  /**
   * Performs the dialog operation for the selected resources.
   *
   * <p>
   *
   * @return true, if the operation was successful, otherwise false
   * @throws CmsException if operation was not successful
   */
  protected abstract boolean performDialogOperation() throws CmsException;
}
/**
 * Helper class which provides static methods for the new documents functions of the document
 * management.
 *
 * <p>
 *
 * @author Andreas Zahner
 * @author Michael Emmerich
 * @version $Revision: 1.3 $
 * @since 6.0.0
 */
public final class NewDocumentsTree {

  /** Session key prefix. */
  protected static final String C_SESSION_KEY_PARAMS = NewDocumentsTree.class.getName() + ".param.";

  /** Session key for search all. */
  public static final String C_DOCUMENT_SEARCH_PARAM_ALL = C_SESSION_KEY_PARAMS + "all";

  /** Session key for categorylist. */
  public static final String C_DOCUMENT_SEARCH_PARAM_CATEGORYLIST =
      C_SESSION_KEY_PARAMS + "categoryList";

  /** Session key for enddate. */
  public static final String C_DOCUMENT_SEARCH_PARAM_ENDDATE = C_SESSION_KEY_PARAMS + "endDate";

  /** Session key for site. */
  public static final String C_DOCUMENT_SEARCH_PARAM_SITE = C_SESSION_KEY_PARAMS + "site";

  /** Session key for startdate. */
  public static final String C_DOCUMENT_SEARCH_PARAM_STARTDATE = C_SESSION_KEY_PARAMS + "startDate";

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(NewDocumentsTree.class);

  /**
   * Hide public constructor.
   *
   * <p>
   */
  private NewDocumentsTree() {

    // noop
  }

  /**
   * Creates an HTML list with checkboxes for all given categories.
   *
   * <p>
   *
   * @param categories list with the categories (LgtCategory)
   * @param attributes optional attributes for the input tags
   * @return the HTML code for a category input list
   */
  public static String buildCategoryList(List<CmsCategory> categories, String attributes) {

    StringBuffer retValue = new StringBuffer(128);
    Iterator<CmsCategory> i = categories.iterator();
    int counter = 0;
    while (i.hasNext()) {
      CmsCategory curCat = i.next();
      if (!"".equals(curCat.getCmsResource())) {
        retValue.append(
            "<input type=\"checkbox\" name=\"cat"
                + counter
                + "\" id=\"cat"
                + counter
                + "\" value=\""
                + curCat.getCmsResource()
                + "\"");
        if (attributes != null) {
          retValue.append(" " + attributes);
        }
        retValue.append(">&nbsp;");
        retValue.append(curCat.getTitle() + "<br>\n");
        counter++;
      }
    }

    // clear objects to release memory
    i = null;

    return retValue.toString();
  }

  /**
   * Builds the HTML code for a select box of days.
   *
   * <p>
   *
   * @param timeMillis the time in milliseconds which should be preselected
   * @param fieldName the prefix of the name attribute
   * @param attributes optional additional attributes of the select tag
   * @param localeString the current locale String
   * @return the HTML code for a select box of days
   */
  public static String buildSelectDay(
      long timeMillis, String fieldName, String attributes, String localeString) {

    StringBuffer retValue = new StringBuffer(512);
    Locale locale = new Locale(localeString);
    Calendar cal = new GregorianCalendar(locale);
    cal.setTimeInMillis(timeMillis);

    retValue.append("<select name=\"" + fieldName + "day\"");
    if (attributes != null) {
      retValue.append(" " + attributes);
    }
    retValue.append(">\n");
    for (int i = 1; i < 32; i++) {
      retValue.append("\t<option value=\"" + i + "\"");
      if (cal.get(Calendar.DAY_OF_MONTH) == i) {
        retValue.append(" selected=\"selected\"");
      }
      retValue.append(">" + i + "</option>\n");
    }
    retValue.append("</select>\n");

    return retValue.toString();
  }

  /**
   * Builds the HTML code for a select box of months.
   *
   * <p>
   *
   * @param timeMillis the time in milliseconds which should be preselected
   * @param fieldName the prefix of the name attribute
   * @param attributes optional additional attributes of the select tag
   * @param localeString the current locale String
   * @return the HTML code for a select box of months
   */
  public static String buildSelectMonth(
      long timeMillis, String fieldName, String attributes, String localeString) {

    StringBuffer retValue = new StringBuffer(512);
    Locale locale = new Locale(localeString);
    Calendar cal = new GregorianCalendar(locale);
    cal.setTimeInMillis(timeMillis);
    Calendar calTemp = new GregorianCalendar(locale);
    calTemp.setTimeInMillis(timeMillis);
    // set day to 2 to avoid display errors for days 29, 30 and 31
    calTemp.set(Calendar.DAY_OF_MONTH, 2);
    DateFormat df = new SimpleDateFormat("MMMM", locale);

    retValue.append("<select name=\"" + fieldName + "month\"");
    if (attributes != null) {
      retValue.append(" " + attributes);
    }
    retValue.append(">\n");
    for (int i = 0; i < 12; i++) {
      calTemp.set(Calendar.MONTH, i);
      retValue.append("\t<option value=\"" + (i + 1) + "\"");
      if (cal.get(Calendar.MONTH) == i) {
        retValue.append(" selected=\"selected\"");
      }
      retValue.append(">" + df.format(calTemp.getTime()) + "</option>\n");
    }
    retValue.append("</select>\n");

    return retValue.toString();
  }

  /**
   * Builds the HTML code for a select box of years.
   *
   * <p>
   *
   * @param timeMillis the time in milliseconds which should be preselected
   * @param fieldName the prefix of the name attribute
   * @param attributes optional additional attributes of the select tag
   * @param localeString the current locale String
   * @param startyear the year to start with
   * @param endyear the last year to display in the selection
   * @return the HTML code for a select box of years
   */
  public static String buildSelectYear(
      long timeMillis,
      String fieldName,
      String attributes,
      String localeString,
      int startyear,
      int endyear) {

    StringBuffer retValue = new StringBuffer(512);
    Locale locale = new Locale(localeString);
    Calendar cal = new GregorianCalendar(locale);
    cal.setTimeInMillis(timeMillis);

    if (startyear > endyear) {
      startyear = endyear;
    }

    retValue.append("<select name=\"" + fieldName + "year\"");
    if (attributes != null) {
      retValue.append(" " + attributes);
    }
    retValue.append(">\n");
    for (int i = startyear; i <= endyear; i++) {
      retValue.append("\t<option value=\"" + i + "\"");
      if (cal.get(Calendar.YEAR) == i) {
        retValue.append(" selected=\"selected\"");
      }
      retValue.append(">" + i + "</option>\n");
    }
    retValue.append("</select>\n");

    return retValue.toString();
  }

  /**
   * Returns a list of resources which contains no linked entries.
   *
   * <p>Links on the same resource entry are deleted from the list of resources. This method has to
   * be used after calling the method CmsObject.getResourcesInTimeRange(String, long, long);
   *
   * @param resources the list of resources which may contain links
   * @return a filtered list of resources
   */
  public static List<CmsResource> filterLinkedResources(List<CmsResource> resources) {

    List<CmsResource> filteredResources = new ArrayList<CmsResource>();
    Set<CmsUUID> addedResources = new HashSet<CmsUUID>();
    long currentTime = System.currentTimeMillis();
    Iterator<CmsResource> i = resources.iterator();

    while (i.hasNext()) {

      CmsResource currentResource = i.next();

      // filter those documents that are folders or outside the release and expire window
      if (currentResource.isFolder()
          || (currentResource.getDateReleased() > currentTime)
          || (currentResource.getDateExpired() < currentTime)) {
        // skip folders and resources outside time range
        continue;
      }

      if (CmsDocumentFactory.isIgnoredDocument(currentResource.getRootPath(), true)) {
        // this resource is ignored, skip it before checking the resource id
        continue;
      }

      CmsUUID resId = currentResource.getResourceId();

      if (!addedResources.contains(resId)) {

        // add resource to return list and ID to set
        addedResources.add(resId);
        filteredResources.add(currentResource);
      }
    }

    // clear objects to release memory
    i = null;
    addedResources = null;
    resources = null;

    return filteredResources;
  }

  /**
   * Returns a String which holds the selected categories for the result page of the new documents
   * query.
   *
   * <p>
   *
   * @param cms the CmsObject
   * @param request the HttpServletRequest
   * @param messageAll the localized message String used when all categories were selected
   * @return String with comma separated selected categories or localized "all" message
   */
  public static String getCategories(CmsObject cms, HttpServletRequest request, String messageAll) {

    StringBuffer retValue = new StringBuffer(128);

    // get the current user's HHTP session
    // HttpSession session =
    // ((HttpServletRequest)cms.getRequestContext().getRequest().getOriginalRequest()).getSession();
    HttpSession session = request.getSession();

    // get the required parameters
    String paramAll = (String) session.getAttribute(C_DOCUMENT_SEARCH_PARAM_ALL);

    if ("true".equals(paramAll)) {
      return messageAll;
    } else {
      List<String> categories =
          getCategoryList((String) session.getAttribute(C_DOCUMENT_SEARCH_PARAM_CATEGORYLIST));
      Iterator<String> i = categories.iterator();
      while (i.hasNext()) {
        String path = i.next();
        try {
          retValue.append(
              cms.readPropertyObject(path, CmsCategory.CATEGORY_TITLE, false).getValue());
        } catch (CmsException e) {
          // noop
        }
        if (i.hasNext()) {
          retValue.append(", ");
        }
      }

      // clear objects to release memory
      categories = null;
      i = null;
    }
    return retValue.toString();
  }

  /**
   * Builds a list of the categories which were selected in the form.
   *
   * <p>
   *
   * @param allCategories String with all selected category paths
   * @return List with all selected categories (holds String objects with absolute paths)
   */
  public static List<String> getCategoryList(String allCategories) {

    ArrayList<String> categories = new ArrayList<String>();

    // get the indiviadual category paths from the token
    StringTokenizer T = new StringTokenizer(allCategories, CategoryTree.C_LIST_SEPARATOR);
    while (T.hasMoreTokens()) {
      String curToken = T.nextToken();
      if (!"".equals(curToken.trim())) {
        // add the category to the list
        categories.add(curToken);
      }
    }
    return categories;
  }

  /**
   * Returns a list of new resources in the specified folder or category folder depending on the
   * request parameters.
   *
   * <p>
   *
   * @param cms the CmsObject to perform some operations
   * @param request the HttpServletRequest to get the needed request parameters
   * @return the list of new resources
   */
  public static List<CmsResource> getNewResources(CmsObject cms, HttpServletRequest request) {

    // get the current user's HHTP session
    HttpSession session = request.getSession();

    String startFolder = (String) request.getAttribute(CmsDocumentFrontend.ATTR_FULLPATH);

    // get the required parameters
    String paramAll = (String) session.getAttribute(C_DOCUMENT_SEARCH_PARAM_ALL);
    String paramStartDate = (String) session.getAttribute(C_DOCUMENT_SEARCH_PARAM_STARTDATE);
    String paramEndDate = (String) session.getAttribute(C_DOCUMENT_SEARCH_PARAM_ENDDATE);

    // parse the date Strings to long
    long startDate = Long.parseLong(paramStartDate);
    long endDate = Long.parseLong(paramEndDate);

    // create list of categories if selected
    List<String> selectedCategoryList = new ArrayList<String>();
    paramAll = (paramAll == null) ? "false" : paramAll;
    if (!"true".equals(paramAll)) {
      // search individual categories
      selectedCategoryList =
          getCategoryList((String) session.getAttribute(C_DOCUMENT_SEARCH_PARAM_CATEGORYLIST));
      if (selectedCategoryList.size() == 0) {
        return new ArrayList<CmsResource>(0);
      }
    }

    String openedCategories =
        CategoryTree.getTreeInfo(cms, CategoryTree.C_USER_INFO_OPENED_CATEGORIES, request);
    List<String> openedCategoryList =
        CategoryTree.commaStringToList(openedCategories, CategoryTree.C_LIST_SEPARATOR);

    return getNewResourceList(
        cms, startFolder, startDate, endDate, selectedCategoryList, openedCategoryList);
  }

  /**
   * Creates a nice localized date String from the given String.
   *
   * <p>
   *
   * @param dateLongString the date as String representation of a long value
   * @param localeString the current locale String
   * @return nice formatted date string in long mode (e.g. 15. April 2003)
   */
  public static String getNiceDate(String dateLongString, String localeString) {

    Locale locale = new Locale(localeString.toLowerCase());
    DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, locale);
    Calendar cal = new GregorianCalendar(locale);
    try {
      cal.setTimeInMillis(Long.parseLong(dateLongString));
    } catch (Exception e) {
      // noop
    }
    return df.format(cal.getTime());
  }

  /**
   * Creates a nice localized date String from the given day, month and year Strings.
   *
   * <p>
   *
   * @param day the number of the day as String
   * @param month the number of the month as String
   * @param year the number of the year as String
   * @param localeString the current locale String
   * @return nice formatted date string in long mode (e.g. 15. April 2003)
   */
  public static String getNiceDate(String day, String month, String year, String localeString) {

    Locale locale = new Locale(localeString.toLowerCase());
    DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, locale);
    Calendar cal = new GregorianCalendar(locale);
    try {
      cal.set(Integer.parseInt(year), Integer.parseInt(month) - 1, Integer.parseInt(day));
    } catch (Exception e) {
      // noop
    }
    return df.format(cal.getTime());
  }

  /**
   * Creates a list of new resources of the specified folder and filters the unwanted resources.
   *
   * <p>If the parameter categoryFolders is an empty list, all new resources are returned, otherwise
   * only those resources which are in a subfolder specified by the list.
   *
   * <p>
   *
   * @param cms the CmsObject
   * @param startFolder the root folder
   * @param startDate the start date in milliseconds
   * @param endDate the end date in milliseconds
   * @param selectedCategories list with selected categories/folders
   * @param openedCategories list with opened categories/folders
   * @return list of new resources
   */
  private static List<CmsResource> getNewResourceList(
      CmsObject cms,
      String startFolder,
      long startDate,
      long endDate,
      List<String> selectedCategories,
      List<String> openedCategories) {

    List<CmsResource> searchResult = null;
    List<CmsResource> foundResources = null;
    Set<CmsUUID> addedResources = null;

    if (LOG.isDebugEnabled()) {

      StringBuffer buf = new StringBuffer();
      for (int i = 0, n = selectedCategories.size(); i < n; i++) {
        buf.append(selectedCategories.get(i));

        if (i < (n - 1)) {
          buf.append(", ");
        }
      }

      LOG.debug("################ INPUT VALUES FOR NEW DOCUMENTS SEARCH");
      LOG.debug("startDate : " + startDate + " " + new java.util.Date(startDate).toString());
      LOG.debug("endDate : " + endDate + " " + new java.util.Date(endDate).toString());
      LOG.debug("startFolder : " + startFolder);
      LOG.debug("categories : " + buf.toString());
    }

    try {

      // get all resources in the site root which are in the time range
      CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION;
      filter = filter.addRequireLastModifiedAfter(startDate);
      filter = filter.addRequireLastModifiedBefore(endDate);
      foundResources = cms.readResources(startFolder, filter);
    } catch (CmsException e) {

      if (LOG.isErrorEnabled()) {
        LOG.error(
            "Error reading resources in time range "
                + new java.util.Date(startDate).toString()
                + " - "
                + new java.util.Date(endDate).toString()
                + " below folder "
                + startFolder,
            e);
      }
      foundResources = Collections.emptyList();
    }

    if (selectedCategories.size() == 0) {

      // return all found resources with filtered links
      searchResult = filterLinkedResources(foundResources);
    } else {

      addedResources = new HashSet<CmsUUID>();
      searchResult = new ArrayList<CmsResource>();
      long currentTime = System.currentTimeMillis();

      for (int i = 0, n = foundResources.size(); i < n; i++) {

        // analyze each resource if it has to be included in the search result

        CmsResource resource = foundResources.get(i);

        // filter those documents that are folders or outside the release and expire window
        if (resource.isFolder()
            || (resource.getDateReleased() > currentTime)
            || (resource.getDateExpired() < currentTime)) {
          // skip folders and resources outside time range
          continue;
        }

        String resourceName = cms.getRequestContext().removeSiteRoot(resource.getRootPath());
        String parentFolder = CmsResource.getParentFolder(resourceName);
        boolean addToResult = false;

        if (!selectedCategories.contains(parentFolder) && openedCategories.contains(parentFolder)) {

          // skip resources that are inside an opened, but un-selected category/folder
          continue;
        }

        // check if the parent folder of the resource is one of the selected categories/folders
        addToResult = selectedCategories.contains(parentFolder);

        if (!addToResult) {

          // check if the resource is inside a collapsed sub-tree
          // of a selected category

          int openedCategoryCount = 0;

          while (!"/".equals(parentFolder)) {

            if (openedCategories.contains(parentFolder)) {
              openedCategoryCount++;
            }

            if (selectedCategories.contains(parentFolder) && (openedCategoryCount == 0)) {

              // we found a selected parent category,
              // and it's sub-tree is collapsed
              addToResult = true;
              break;
            }

            parentFolder = CmsResource.getParentFolder(parentFolder);
          }
        }

        if (!addToResult) {

          // continue with the next resource
          continue;
        }

        if (CmsDocumentFactory.isIgnoredDocument(resourceName, true)) {
          // this resource is ignored, skip it before checking the resource id
          continue;
        }

        // check if the resource is a sibling that has already been added to the search result
        CmsUUID resourceId = resource.getResourceId();
        if (!addedResources.contains(resourceId)) {

          // add resource to the result
          addedResources.add(resourceId);
          searchResult.add(resource);
        }
      }
    }

    return searchResult;
  }
}
/**
 * Collector to provide {@link CmsResource} objects for a explorer List.
 *
 * <p>
 *
 * @since 6.1.0
 */
public abstract class A_CmsListResourceCollector implements I_CmsListResourceCollector {

  /** VFS path to use for a dummy resource object. */
  public static final String VFS_PATH_NONE = "none";

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(A_CmsListResourceCollector.class);

  /** The collector parameter. */
  protected String m_collectorParameter;

  /** List item cache. */
  protected Map<String, CmsListItem> m_liCache = new HashMap<String, CmsListItem>();

  /** Resource cache. */
  protected Map<String, CmsResource> m_resCache = new HashMap<String, CmsResource>();

  /** Cache for resource list result. */
  protected List<CmsResource> m_resources;

  /** The workplace object where the collector is used from. */
  private A_CmsListExplorerDialog m_wp;

  /**
   * Constructor, creates a new list collector.
   *
   * <p>
   *
   * @param wp the workplace object where the collector is used from
   */
  protected A_CmsListResourceCollector(A_CmsListExplorerDialog wp) {

    m_wp = wp;
    CmsListState state = (wp != null ? wp.getListStateForCollector() : new CmsListState());
    if (state.getPage() < 1) {
      state.setPage(1);
    }
    if (CmsStringUtil.isEmptyOrWhitespaceOnly(state.getColumn())) {
      state.setColumn(A_CmsListExplorerDialog.LIST_COLUMN_NAME);
    }
    if (state.getOrder() == null) {
      state.setOrder(CmsListOrderEnum.ORDER_ASCENDING);
    }
    if (state.getFilter() == null) {
      state.setFilter("");
    }
    m_collectorParameter =
        I_CmsListResourceCollector.PARAM_PAGE
            + I_CmsListResourceCollector.SEP_KEYVAL
            + state.getPage();
    m_collectorParameter +=
        I_CmsListResourceCollector.SEP_PARAM
            + I_CmsListResourceCollector.PARAM_SORTBY
            + I_CmsListResourceCollector.SEP_KEYVAL
            + state.getColumn();
    m_collectorParameter +=
        I_CmsListResourceCollector.SEP_PARAM
            + I_CmsListResourceCollector.PARAM_ORDER
            + I_CmsListResourceCollector.SEP_KEYVAL
            + state.getOrder();
    m_collectorParameter +=
        I_CmsListResourceCollector.SEP_PARAM
            + I_CmsListResourceCollector.PARAM_FILTER
            + I_CmsListResourceCollector.SEP_KEYVAL
            + state.getFilter();
  }

  /** @see java.lang.Comparable#compareTo(java.lang.Object) */
  public int compareTo(I_CmsResourceCollector arg0) {

    return 0;
  }

  /**
   * @see
   *     org.opencms.file.collectors.I_CmsResourceCollector#getCreateLink(org.opencms.file.CmsObject)
   */
  public String getCreateLink(CmsObject cms) {

    return null;
  }

  /**
   * @see
   *     org.opencms.file.collectors.I_CmsResourceCollector#getCreateLink(org.opencms.file.CmsObject,
   *     java.lang.String, java.lang.String)
   */
  public String getCreateLink(CmsObject cms, String collectorName, String param) {

    return null;
  }

  /**
   * @see
   *     org.opencms.file.collectors.I_CmsResourceCollector#getCreateParam(org.opencms.file.CmsObject)
   */
  public String getCreateParam(CmsObject cms) {

    return null;
  }

  /**
   * @see
   *     org.opencms.file.collectors.I_CmsResourceCollector#getCreateParam(org.opencms.file.CmsObject,
   *     java.lang.String, java.lang.String)
   */
  public String getCreateParam(CmsObject cms, String collectorName, String param) {

    return null;
  }

  /** @see org.opencms.file.collectors.I_CmsResourceCollector#getDefaultCollectorName() */
  public String getDefaultCollectorName() {

    return getCollectorNames().get(0);
  }

  /** @see org.opencms.file.collectors.I_CmsResourceCollector#getDefaultCollectorParam() */
  public String getDefaultCollectorParam() {

    return m_collectorParameter;
  }

  /**
   * Returns a list of list items from a list of resources.
   *
   * <p>
   *
   * @param parameter the collector parameter or <code>null</code> for default.
   *     <p>
   * @return a list of {@link CmsListItem} objects
   * @throws CmsException if something goes wrong
   */
  public List<CmsListItem> getListItems(String parameter) throws CmsException {

    synchronized (this) {
      if (parameter == null) {
        parameter = m_collectorParameter;
      }
      Map<String, String> params =
          CmsStringUtil.splitAsMap(
              parameter,
              I_CmsListResourceCollector.SEP_PARAM,
              I_CmsListResourceCollector.SEP_KEYVAL);
      CmsListState state = getState(params);
      List<CmsResource> resources = getInternalResources(getWp().getCms(), params);
      List<CmsListItem> ret = new ArrayList<CmsListItem>();
      if (LOG.isDebugEnabled()) {
        LOG.debug(
            Messages.get()
                .getBundle()
                .key(Messages.LOG_COLLECTOR_PROCESS_ITEMS_START_1, new Integer(resources.size())));
      }
      getWp().applyColumnVisibilities();
      CmsHtmlList list = getWp().getList();

      // check if progress should be set in the thread
      CmsProgressThread thread = null;
      int progressOffset = 0;
      if (Thread.currentThread() instanceof CmsProgressThread) {
        thread = (CmsProgressThread) Thread.currentThread();
        progressOffset = thread.getProgress();
      }

      CmsListColumnDefinition colPermissions =
          list.getMetadata().getColumnDefinition(A_CmsListExplorerDialog.LIST_COLUMN_PERMISSIONS);
      boolean showPermissions = (colPermissions.isVisible() || colPermissions.isPrintable());
      CmsListColumnDefinition colDateLastMod =
          list.getMetadata().getColumnDefinition(A_CmsListExplorerDialog.LIST_COLUMN_DATELASTMOD);
      boolean showDateLastMod = (colDateLastMod.isVisible() || colDateLastMod.isPrintable());
      CmsListColumnDefinition colUserLastMod =
          list.getMetadata().getColumnDefinition(A_CmsListExplorerDialog.LIST_COLUMN_USERLASTMOD);
      boolean showUserLastMod = (colUserLastMod.isVisible() || colUserLastMod.isPrintable());
      CmsListColumnDefinition colDateCreate =
          list.getMetadata().getColumnDefinition(A_CmsListExplorerDialog.LIST_COLUMN_DATECREATE);
      boolean showDateCreate = (colDateCreate.isVisible() || colDateCreate.isPrintable());
      CmsListColumnDefinition colUserCreate =
          list.getMetadata().getColumnDefinition(A_CmsListExplorerDialog.LIST_COLUMN_USERCREATE);
      boolean showUserCreate = (colUserCreate.isVisible() || colUserCreate.isPrintable());
      CmsListColumnDefinition colDateRel =
          list.getMetadata().getColumnDefinition(A_CmsListExplorerDialog.LIST_COLUMN_DATEREL);
      boolean showDateRel = (colDateRel.isVisible() || colDateRel.isPrintable());
      CmsListColumnDefinition colDateExp =
          list.getMetadata().getColumnDefinition(A_CmsListExplorerDialog.LIST_COLUMN_DATEEXP);
      boolean showDateExp = (colDateExp.isVisible() || colDateExp.isPrintable());
      CmsListColumnDefinition colState =
          list.getMetadata().getColumnDefinition(A_CmsListExplorerDialog.LIST_COLUMN_STATE);
      boolean showState = (colState.isVisible() || colState.isPrintable());
      CmsListColumnDefinition colLockedBy =
          list.getMetadata().getColumnDefinition(A_CmsListExplorerDialog.LIST_COLUMN_LOCKEDBY);
      boolean showLockedBy = (colLockedBy.isVisible() || colLockedBy.isPrintable());
      CmsListColumnDefinition colSite =
          list.getMetadata().getColumnDefinition(A_CmsListExplorerDialog.LIST_COLUMN_SITE);
      boolean showSite = (colSite.isVisible() || colSite.isPrintable());

      // get content
      Iterator<CmsResource> itRes = resources.iterator();
      int count = 0;
      while (itRes.hasNext()) {
        // set progress in thread
        if (thread != null) {
          count++;
          if (thread.isInterrupted()) {
            throw new CmsIllegalStateException(
                org.opencms.workplace.commons.Messages.get()
                    .container(org.opencms.workplace.commons.Messages.ERR_PROGRESS_INTERRUPTED_0));
          }
          thread.setProgress(((count * 40) / resources.size()) + progressOffset);
          thread.setDescription(
              org.opencms.workplace.commons.Messages.get()
                  .getBundle(thread.getLocale())
                  .key(
                      org.opencms.workplace.commons.Messages.GUI_PROGRESS_PUBLISH_STEP2_2,
                      new Integer(count),
                      new Integer(resources.size())));
        }

        Object obj = itRes.next();
        if (!(obj instanceof CmsResource)) {
          ret.add(getDummyListItem(list));
          continue;
        }
        CmsResource resource = (CmsResource) obj;
        CmsListItem item = m_liCache.get(resource.getStructureId().toString());
        if (item == null) {
          item =
              createResourceListItem(
                  resource,
                  list,
                  showPermissions,
                  showDateLastMod,
                  showUserLastMod,
                  showDateCreate,
                  showUserCreate,
                  showDateRel,
                  showDateExp,
                  showState,
                  showLockedBy,
                  showSite);
          m_liCache.put(resource.getStructureId().toString(), item);
        }
        ret.add(item);
      }
      CmsListMetadata metadata = list.getMetadata();
      if (metadata != null) {
        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(state.getFilter())) {
          // filter
          ret = metadata.getSearchAction().filter(ret, state.getFilter());
        }
        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(state.getColumn())) {
          if ((metadata.getColumnDefinition(state.getColumn()) != null)
              && metadata.getColumnDefinition(state.getColumn()).isSorteable()) {
            // sort
            I_CmsListItemComparator c =
                metadata.getColumnDefinition(state.getColumn()).getListItemComparator();
            Collections.sort(ret, c.getComparator(state.getColumn(), getWp().getLocale()));
            if (state.getOrder().equals(CmsListOrderEnum.ORDER_DESCENDING)) {
              Collections.reverse(ret);
            }
          }
        }
      }
      if (LOG.isDebugEnabled()) {
        LOG.debug(
            Messages.get()
                .getBundle()
                .key(Messages.LOG_COLLECTOR_PROCESS_ITEMS_END_1, new Integer(ret.size())));
      }
      return ret;
    }
  }

  /** @see org.opencms.file.collectors.I_CmsResourceCollector#getOrder() */
  public int getOrder() {

    return 0;
  }

  /**
   * Returns the resource for the given item.
   *
   * <p>
   *
   * @param cms the cms object
   * @param item the item
   * @return the resource
   */
  public CmsResource getResource(CmsObject cms, CmsListItem item) {

    CmsResource res = m_resCache.get(item.getId());
    if (res == null) {
      CmsUUID id = new CmsUUID(item.getId());
      if (!id.isNullUUID()) {
        try {
          res = cms.readResource(id, CmsResourceFilter.ALL);
          m_resCache.put(item.getId(), res);
        } catch (CmsException e) {
          // should never happen
          if (LOG.isErrorEnabled()) {
            LOG.error(e.getLocalizedMessage(), e);
          }
        }
      }
    }
    return res;
  }

  /**
   * Returns all, unsorted and unfiltered, resources.
   *
   * <p>Be sure to cache the resources.
   *
   * <p>
   *
   * @param cms the cms object
   * @param params the parameter map
   * @return a list of {@link CmsResource} objects
   * @throws CmsException if something goes wrong
   */
  public abstract List<CmsResource> getResources(CmsObject cms, Map<String, String> params)
      throws CmsException;

  /**
   * @see org.opencms.file.collectors.I_CmsResourceCollector#getResults(org.opencms.file.CmsObject)
   */
  public List<CmsResource> getResults(CmsObject cms) throws CmsException {

    return getResults(cms, getDefaultCollectorName(), m_collectorParameter);
  }

  /**
   * The parameter must follow the syntax "page:nr" where nr is the number of the page to be
   * displayed.
   *
   * <p>
   *
   * @see org.opencms.file.collectors.I_CmsResourceCollector#getResults(org.opencms.file.CmsObject,
   *     java.lang.String, java.lang.String)
   */
  public List<CmsResource> getResults(CmsObject cms, String collectorName, String parameter)
      throws CmsException {

    synchronized (this) {
      if (LOG.isDebugEnabled()) {
        LOG.debug(Messages.get().getBundle().key(Messages.LOG_COLLECTOR_GET_RESULTS_START_0));
      }
      if (parameter == null) {
        parameter = m_collectorParameter;
      }
      List<CmsResource> resources = new ArrayList<CmsResource>();
      if (getWp().getList() != null) {
        Iterator<CmsListItem> itItems = getListItems(parameter).iterator();
        while (itItems.hasNext()) {
          CmsListItem item = itItems.next();
          resources.add(getResource(cms, item));
        }
      } else {
        Map<String, String> params =
            CmsStringUtil.splitAsMap(
                parameter,
                I_CmsListResourceCollector.SEP_PARAM,
                I_CmsListResourceCollector.SEP_KEYVAL);
        resources = getInternalResources(cms, params);
      }
      if (LOG.isDebugEnabled()) {
        LOG.debug(
            Messages.get()
                .getBundle()
                .key(Messages.LOG_COLLECTOR_GET_RESULTS_END_1, new Integer(resources.size())));
      }
      return resources;
    }
  }

  /**
   * Returns the workplace object.
   *
   * <p>
   *
   * @return the workplace object
   */
  public A_CmsListExplorerDialog getWp() {

    return m_wp;
  }

  /**
   * @see
   *     org.opencms.file.collectors.I_CmsResourceCollector#setDefaultCollectorName(java.lang.String)
   */
  public void setDefaultCollectorName(String collectorName) {

    // ignore
  }

  /**
   * The parameter must follow the syntax "mode|projectId" where mode is either "new", "changed",
   * "deleted" or "modified" and projectId is the id of the project to be displayed.
   *
   * <p>
   *
   * @see
   *     org.opencms.file.collectors.I_CmsResourceCollector#setDefaultCollectorParam(java.lang.String)
   */
  public void setDefaultCollectorParam(String param) {

    m_collectorParameter = param;
  }

  /** @see org.opencms.file.collectors.I_CmsResourceCollector#setOrder(int) */
  public void setOrder(int order) {

    // ignore
  }

  /**
   * Sets the current display page.
   *
   * <p>
   *
   * @param page the new display page
   */
  public void setPage(int page) {

    if (m_collectorParameter != null) {
      int pos = m_collectorParameter.indexOf(I_CmsListResourceCollector.PARAM_PAGE);
      if (pos >= 0) {
        String params = "";
        int endPos = m_collectorParameter.indexOf(I_CmsListResourceCollector.SEP_PARAM, pos);
        if (pos > 0) {
          pos -= I_CmsListResourceCollector.SEP_PARAM.length(); // remove also the SEP_PARAM
          params += m_collectorParameter.substring(0, pos);
        }
        if (endPos >= 0) {
          if (pos == 0) {
            endPos += I_CmsListResourceCollector.SEP_PARAM.length(); // remove also the SEP_PARAM
          }
          params += m_collectorParameter.substring(endPos, m_collectorParameter.length());
        }
        m_collectorParameter = params;
      }
    }
    if (m_collectorParameter == null) {
      m_collectorParameter = "";
    } else if (m_collectorParameter.length() > 0) {
      m_collectorParameter += I_CmsListResourceCollector.SEP_PARAM;
    }
    m_collectorParameter +=
        I_CmsListResourceCollector.PARAM_PAGE + I_CmsListResourceCollector.SEP_KEYVAL + page;
    synchronized (this) {
      m_resources = null;
    }
  }

  /**
   * Returns a list item created from the resource information, differs between valid resources and
   * invalid resources.
   *
   * <p>
   *
   * @param resource the resource to create the list item from
   * @param list the list
   * @param showPermissions if to show permissions
   * @param showDateLastMod if to show the last modification date
   * @param showUserLastMod if to show the last modification user
   * @param showDateCreate if to show the creation date
   * @param showUserCreate if to show the creation date
   * @param showDateRel if to show the date released
   * @param showDateExp if to show the date expired
   * @param showState if to show the state
   * @param showLockedBy if to show the lock user
   * @param showSite if to show the site
   * @return a list item created from the resource information
   */
  protected CmsListItem createResourceListItem(
      CmsResource resource,
      CmsHtmlList list,
      boolean showPermissions,
      boolean showDateLastMod,
      boolean showUserLastMod,
      boolean showDateCreate,
      boolean showUserCreate,
      boolean showDateRel,
      boolean showDateExp,
      boolean showState,
      boolean showLockedBy,
      boolean showSite) {

    CmsListItem item = list.newItem(resource.getStructureId().toString());
    // get an initialized resource utility
    CmsResourceUtil resUtil = getWp().getResourceUtil();
    resUtil.setResource(resource);
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_NAME, resUtil.getPath());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_ROOT_PATH, resUtil.getFullPath());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_TITLE, resUtil.getTitle());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_TYPE, resUtil.getResourceTypeName());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_SIZE, resUtil.getSizeString());
    if (showPermissions) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_PERMISSIONS, resUtil.getPermissionString());
    }
    if (showDateLastMod) {
      item.set(
          A_CmsListExplorerDialog.LIST_COLUMN_DATELASTMOD,
          new Date(resource.getDateLastModified()));
    }
    if (showUserLastMod) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_USERLASTMOD, resUtil.getUserLastModified());
    }
    if (showDateCreate) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_DATECREATE, new Date(resource.getDateCreated()));
    }
    if (showUserCreate) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_USERCREATE, resUtil.getUserCreated());
    }
    if (showDateRel) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_DATEREL, new Date(resource.getDateReleased()));
    }
    if (showDateExp) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_DATEEXP, new Date(resource.getDateExpired()));
    }
    if (showState) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_STATE, resUtil.getStateName());
    }
    if (showLockedBy) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_LOCKEDBY, resUtil.getLockedByName());
    }
    if (showSite) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_SITE, resUtil.getSiteTitle());
    }
    setAdditionalColumns(item, resUtil);
    return item;
  }

  /**
   * Returns a dummy list item.
   *
   * <p>
   *
   * @param list the list object to create the entry for
   * @return a dummy list item
   */
  protected CmsListItem getDummyListItem(CmsHtmlList list) {

    CmsListItem item = list.newItem(CmsUUID.getNullUUID().toString());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_NAME, "");
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_TITLE, "");
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_TYPE, "");
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_SIZE, "");
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_PERMISSIONS, "");
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_DATELASTMOD, new Date());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_USERLASTMOD, "");
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_DATECREATE, new Date());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_USERCREATE, "");
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_DATEREL, new Date());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_DATEEXP, new Date());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_STATE, "");
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_LOCKEDBY, "");
    return item;
  }

  /**
   * Wrapper method for caching the result of {@link #getResources(CmsObject, Map)}.
   *
   * <p>
   *
   * @param cms the cms object
   * @param params the parameter map
   * @return the result of {@link #getResources(CmsObject, Map)}
   * @throws CmsException if something goes wrong
   */
  protected List<CmsResource> getInternalResources(CmsObject cms, Map<String, String> params)
      throws CmsException {

    synchronized (this) {
      if (m_resources == null) {
        m_resources = getResources(cms, params);
        Iterator<CmsResource> it = m_resources.iterator();
        while (it.hasNext()) {
          CmsResource resource = it.next();
          m_resCache.put(resource.getStructureId().toString(), resource);
        }
      }
    }
    return m_resources;
  }

  /**
   * Returns the list of resource names from the parameter map.
   *
   * <p>
   *
   * @param params the parameter map
   * @return the list of resource names
   * @see I_CmsListResourceCollector#PARAM_RESOURCES
   */
  protected List<String> getResourceNamesFromParam(Map<String, String> params) {

    String resourcesParam = "/";
    if (params.containsKey(I_CmsListResourceCollector.PARAM_RESOURCES)) {
      resourcesParam = params.get(I_CmsListResourceCollector.PARAM_RESOURCES);
    }
    if (resourcesParam.length() == 0) {
      return Collections.emptyList();
    }
    return CmsStringUtil.splitAsList(resourcesParam, "#");
  }

  /**
   * Returns the state of the parameter map.
   *
   * <p>
   *
   * @param params the parameter map
   * @return the state of the list from the parameter map
   */
  protected CmsListState getState(Map<String, String> params) {

    CmsListState state = new CmsListState();
    try {
      state.setPage(Integer.parseInt(params.get(I_CmsListResourceCollector.PARAM_PAGE)));
    } catch (Throwable e) {
      // ignore
    }
    try {
      state.setOrder(CmsListOrderEnum.valueOf(params.get(I_CmsListResourceCollector.PARAM_ORDER)));
    } catch (Throwable e) {
      // ignore
    }
    try {
      state.setFilter(params.get(I_CmsListResourceCollector.PARAM_FILTER));
    } catch (Throwable e) {
      // ignore
    }
    try {
      state.setColumn(params.get(I_CmsListResourceCollector.PARAM_SORTBY));
    } catch (Throwable e) {
      // ignore
    }
    return state;
  }

  /**
   * Set additional column entries for a resource.
   *
   * <p>Overwrite this method to set additional column entries.
   *
   * <p>
   *
   * @param item the current list item
   * @param resUtil the resource util object for getting the info from
   */
  protected abstract void setAdditionalColumns(CmsListItem item, CmsResourceUtil resUtil);

  /**
   * Sets the resources parameter.
   *
   * <p>
   *
   * @param resources the list of resource names to use
   */
  protected void setResourcesParam(List<String> resources) {

    m_collectorParameter +=
        I_CmsListResourceCollector.SEP_PARAM
            + I_CmsListResourceCollector.PARAM_RESOURCES
            + I_CmsListResourceCollector.SEP_KEYVAL;
    if (resources == null) {
      // search anywhere
      m_collectorParameter += "/";
    } else {
      m_collectorParameter += CmsStringUtil.collectionAsString(resources, "#");
    }
  }
}
/**
 * Wrapper for the FreeMarker template engine containing macros that are used to generate HTML
 * output.
 *
 * <p>Use this class with caution! It might be moved to the OpenCms core packages in the future.
 *
 * <p>
 *
 * @since 6.2.0
 */
public class CmsMacroWrapperFreeMarker implements I_CmsMacroWrapper {

  /** File suffix for template files. */
  public static final String FILE_SUFFIX = "ftl";

  /** Variable name for the macro to execute. */
  protected static final String MACRO_NAME = "ocmsmacro";

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsMacroWrapperFreeMarker.class);

  /** The FreeMarker root map. */
  private Map m_rootMap;

  /** The FreeMarker template instance. */
  private Template m_template;

  /**
   * Constructor, with parameters.
   *
   * <p>
   *
   * @param cms the OpenCms user context to use
   * @param macroFile the OpenCms VFS path of the macro template file to use
   * @throws Exception if the initialization of the macro template engine fails
   */
  public CmsMacroWrapperFreeMarker(CmsObject cms, String macroFile) throws Exception {

    // initialize member variables
    init(cms, macroFile);
  }

  /** @see org.opencms.frontend.layoutpage.I_CmsMacroWrapper#getFileSuffix() */
  public String getFileSuffix() {

    return FILE_SUFFIX;
  }

  /** @see org.opencms.frontend.layoutpage.I_CmsMacroWrapper#getResult(java.lang.String) */
  public String getResult(String macroName) {

    return getResult(macroName, null);
  }

  /**
   * @see org.opencms.frontend.layoutpage.I_CmsMacroWrapper#getResult(java.lang.String,
   *     java.lang.String[])
   */
  public String getResult(String macroName, String[] args) {

    Writer out = new StringWriter();
    boolean error = false;
    try {
      // get the macro object to process
      Macro macro = (Macro) m_template.getMacros().get(macroName);
      if (macro != null) {
        // found macro, put it context
        putContextVariable(MACRO_NAME, macro);
        // process the template
        m_template.process(getContext(), out);
      } else {
        // did not find macro
        error = true;
      }
    } catch (Exception e) {
      if (LOG.isErrorEnabled()) {
        LOG.error(e.getLocalizedMessage(), e);
      }
      error = true;
    } finally {
      try {
        out.close();
      } catch (Exception e) {
        // ignore exception when closing writer
      }
    }
    if (error) {
      return "";
    }
    return out.toString();
  }

  /**
   * @see org.opencms.frontend.layoutpage.I_CmsMacroWrapper#init(org.opencms.file.CmsObject,
   *     java.lang.String)
   */
  public void init(CmsObject cms, String macroFile) throws Exception {

    // create the root map context that can be used
    m_rootMap = new HashMap(16);

    // get the template cache instance
    CmsFreeMarkerTemplateCache cache = CmsFreeMarkerTemplateCache.getInstance();
    // get the template from the cache
    m_template = cache.getTemplate(cms, macroFile);
  }

  /**
   * @see org.opencms.frontend.layoutpage.I_CmsMacroWrapper#putContextVariable(java.lang.String,
   *     java.lang.Object)
   */
  public Object putContextVariable(String key, Object value) {

    return getContext().put(key, value);
  }

  /**
   * @see org.opencms.frontend.layoutpage.I_CmsMacroWrapper#removeContextVariable(java.lang.String)
   */
  public Object removeContextVariable(String key) {

    return getContext().remove(key);
  }

  /**
   * Returns an initialized context root map.
   *
   * <p>
   *
   * @return an initialized context root map
   */
  private Map getContext() {

    return m_rootMap;
  }
}
/**
 * example: <br>
 * &lt;%@ taglib &nbsp; prefix="ocms" &nbsp; uri="http://www.langhua.cn/taglib/display"%&gt;<br>
 * &lt;%ocms:navigationbargraphic &nbsp; linkpath="/home/link/easy" &nbsp; cssfile="/home/css/test"
 * /&gt;<br>
 * linkpath : this is taglib's content.<br>
 * cssfile: the css config
 *
 * <p>Image name: Navigation-display.PNG<br>
 */
public class NavigationBarGraphic extends A_LanghuaTag {

  private static final long serialVersionUID = 1L;

  private static final Log LOG = CmsLog.getLog(NavigationBarGraphic.class);

  public int doStartTag() throws JspTagException {

    String htmlbody = buildHtml();
    try {

      pageContext.getOut().print(htmlbody);
    } catch (Exception e) {

      if (LOG.isDebugEnabled()) {
        LOG.debug(e);
      }
    }

    return SKIP_BODY;
  }

  public class TitleFont {
    String titlefont, path;

    public String getTitlefont() {
      return titlefont;
    }

    public void setTitlefont(String titlefont) {
      this.titlefont = titlefont;
    }

    public String getPath() {
      return path;
    }

    public void setPath(String path) {
      this.path = path;
    }
  }

  public String buildHtml() {
    StringBuffer html = new StringBuffer(512);
    HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
    HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
    CmsJspActionElement cms = new CmsJspActionElement(pageContext, request, response);
    CmsJspXmlContentBean cmsx = new CmsJspXmlContentBean(pageContext, request, response);
    Locale locale = cms.getRequestContext().getLocale();
    String folderpath1 = "";
    String folderpath2 = "";
    int mwidth = 0;
    int iwidth = 0;
    int fwidth = 0;
    int twidth = 0;
    int fs = 0;
    int zf = 0;
    boolean have = false;
    try {
      String cssfile = cms.getRequestContext().removeSiteRoot(getCssFile());
      String linkpath = cms.getRequestContext().removeSiteRoot(getLinkFile());
      CmsObject obj = cms.getCmsObject();
      CmsFile config = obj.readFile(cssfile, CmsResourceFilter.IGNORE_EXPIRATION);
      CmsXmlContent configuration = CmsXmlContentFactory.unmarshal(obj, config);
      String mainwidth = configuration.getStringValue(obj, "main.width", locale);
      String imgwidth = configuration.getStringValue(obj, "img.width", locale);
      String trmargin = configuration.getStringValue(obj, "tr.margin", locale);
      String fontsize = configuration.getStringValue(obj, "font.size", locale);

      String imguprowbgimage = configuration.getStringValue(obj, "img.uprow.bgimage", locale);
      String imgdownrowbgimage = configuration.getStringValue(obj, "img.downrow.bgimage", locale);
      List lt = new ArrayList();

      I_CmsXmlContentContainer container;

      container = cmsx.contentload("singleFile", linkpath, true);

      while (container.hasMoreResources()) {

        I_CmsXmlContentContainer container1 = cmsx.contentloop(container, "ProgramLink");

        for (int number = 0; container1.hasMoreResources() && number < 2; number++) {

          if (number == 0) {
            folderpath1 = cmsx.contentshow(container1, "ProgramLink");
          } else {
            folderpath2 = cmsx.contentshow(container1, "ProgramLink");
          }
        }
      }

      String fileName = config.getName();
      if (CmsStringUtil.isEmpty(getCssIndicator())) {
        html.append("<style type=\"text/css\">\n");
        html.append("<!--");
        html.append(buildCSS(cms, cssfile));
        html.append("-->");
        html.append("</style>\n");
      }
      if (CmsStringUtil.isNotEmpty(getCssIndicator())
          && A_LanghuaTag.CSS_INDICATOR_NOSTYLE.equals(getCssIndicator())) {

        html.append(buildCSS(cms, cssfile));
      }
      if (CmsStringUtil.isEmpty(getCssIndicator())
          || A_LanghuaTag.CSS_INDICATOR_CUSTOMIZED.equals(getCssIndicator())) {
        html.append("<div align=\"center\">\n");
        html.append("<div class=\"content" + fileName + "\">\n");
        html.append("<div class=\"img" + fileName + "\">");
        html.append("</div>");
        if (CmsStringUtil.isNotEmpty(mainwidth) && CmsStringUtil.isNotEmpty(imgwidth)) {
          mwidth = Integer.parseInt(mainwidth);
          iwidth = Integer.parseInt(imgwidth);
          fwidth = mwidth - iwidth;
        }
        if (CmsStringUtil.isNotEmpty(trmargin)) {
          twidth = Integer.parseInt(trmargin);
        }
        if (CmsStringUtil.isNotEmpty(fontsize)) {
          fs = Integer.parseInt(fontsize);
        }
        if (CmsStringUtil.isEmpty(imguprowbgimage)) {
          html.append("<div class=\"uprow" + fileName + "\" >");
        } else {
          String uprowuri = cms.link(imguprowbgimage);
          html.append(
              "<div class=\"uprow" + fileName + "\" style=\"background:url(" + uprowuri + ");\">");
        }
        if (CmsStringUtil.isNotEmpty(folderpath1)) {

          List<CmsJspNavElement> list =
              new CmsJspNavBuilder(cms.getCmsObject()).getNavigationForFolder(folderpath1);
          for (int j = 0; j < list.size(); j++) {
            CmsJspNavElement nav = (CmsJspNavElement) list.get(j);
            String ntitle = nav.getNavText();
            String npath = cms.link(nav.getResourceName());
            int nl = ntitle.length();
            nl = nl * fs;
            zf = zf + twidth + nl;
            if (zf > fwidth && (fwidth != 0)) {
              have = true;
            }
            if (have) {
              TitleFont titfont = new TitleFont();
              titfont.setTitlefont(ntitle);
              titfont.setPath(npath);
              lt.add(titfont);

            } else {
              html.append("<div class=\"tr" + fileName + "\">");
              html.append("<a href=\"" + npath + "\"  class=\"uplink" + fileName + "\">");
              html.append(ntitle);
              html.append("</a>");
              html.append("</div>");
            }
          }
        }

        I_CmsXmlContentContainer container0;

        container0 = cmsx.contentload("singleFile", linkpath, true);

        while (container0.hasMoreResources()) {

          I_CmsXmlContentContainer container1 = cmsx.contentloop(container0, "SuperLink");

          while (container1.hasMoreResources()) {
            String description = cmsx.contentshow(container1, "Description");
            String uri = cmsx.contentshow(container1, "SuperLink");
            uri = cms.getRequestContext().removeSiteRoot(uri);
            String id = cmsx.contentshow(container1, "ID");
            if ("1".equals(id)) {
              html.append("<div class=\"tr" + fileName + "\">");
              html.append("<a href=\"" + uri + "\" class=\"uplink" + fileName + "\">\n");
              html.append(description);
              html.append("</a>\n");
              html.append("</div>");
            }
          }
        }
        html.append("</div>");
        if (CmsStringUtil.isEmpty(imgdownrowbgimage)) {
          html.append("<div class=\"downrow" + fileName + "\" >");
        } else {
          String downrowuri = cms.link(imgdownrowbgimage);
          html.append(
              "<div class=\"downrow"
                  + fileName
                  + "\" style=\"background:url("
                  + downrowuri
                  + ");\">");
        }
        ListIterator iterator = lt.listIterator();
        if (have) {
          while (iterator.hasNext()) {
            TitleFont titfont = new TitleFont();
            titfont = (TitleFont) iterator.next();

            html.append("<div class=\"tr" + fileName + "\">");
            html.append(
                "<a href=\"" + titfont.getPath() + "\"  class=\"downlink" + fileName + "\">");
            html.append(titfont.getTitlefont());
            html.append("</a>");
            html.append("</div>");
          }
        }
        if (CmsStringUtil.isNotEmpty(folderpath2)) {
          List<CmsJspNavElement> list =
              new CmsJspNavBuilder(cms.getCmsObject()).getNavigationForFolder(folderpath2);
          for (int j = 0; j < list.size(); j++) {
            CmsJspNavElement nav = (CmsJspNavElement) list.get(j);
            html.append("<div class=\"tr" + fileName + "\">");
            html.append(
                "<a href=\""
                    + cms.link(nav.getResourceName())
                    + "\"  class=\"downlink"
                    + fileName
                    + "\">");
            html.append(nav.getNavText());
            html.append("</a>");
            html.append("</div>");
          }
        }
        I_CmsXmlContentContainer container3;

        container3 = cmsx.contentload("singleFile", linkpath, true);

        while (container3.hasMoreResources()) {

          I_CmsXmlContentContainer container1 = cmsx.contentloop(container3, "SuperLink");

          while (container1.hasMoreResources()) {
            String description = cmsx.contentshow(container1, "Description");
            String uri = cmsx.contentshow(container1, "SuperLink");
            String id = cmsx.contentshow(container1, "ID");
            if ("2".equals(id)) {
              html.append("<div class=\"tr" + fileName + "\">");
              html.append("<a href=\"" + uri + "\" class=\"downlink" + fileName + "\">\n");
              html.append(description);
              html.append("</a>\n");
              html.append("</div>");
            }
          }
        }

        html.append("</div>");
        html.append("</div>");

        html.append("</div>");
      }
    } catch (Exception e1) {

      if (LOG.isDebugEnabled()) {
        LOG.debug(e1);
      }
    }

    return html.toString();
  }
}
/**
 * Class to upload a module with HTTP upload.
 *
 * <p>
 *
 * @since 6.0.0
 */
public class CmsModulesUploadFromHttp extends A_CmsImportFromHttp {

  /** The dialog URI. */
  public static final String DIALOG_URI = PATH_WORKPLACE + "admin/modules/modules_import.jsp";

  /** Modulename parameter. */
  public static final String PARAM_MODULE = "module";

  /** The log object for this class. */
  private static final Log LOG = CmsLog.getLog(CmsModulesUploadFromHttp.class);

  /**
   * Public constructor with JSP action element.
   *
   * <p>
   *
   * @param jsp an initialized JSP action element
   */
  public CmsModulesUploadFromHttp(CmsJspActionElement jsp) {

    super(jsp);
  }

  /**
   * Public constructor with JSP variables.
   *
   * <p>
   *
   * @param context the JSP page context
   * @param req the JSP request
   * @param res the JSP response
   */
  public CmsModulesUploadFromHttp(
      PageContext context, HttpServletRequest req, HttpServletResponse res) {

    this(new CmsJspActionElement(context, req, res));
  }

  /** @see org.opencms.workplace.administration.A_CmsImportFromHttp#actionCommit() */
  public void actionCommit() throws IOException, ServletException {

    try {
      copyFileToServer(
          OpenCms.getSystemInfo().getPackagesRfsPath()
              + File.separator
              + CmsSystemInfo.FOLDER_MODULES);
    } catch (CmsException e) {
      // error copying the file to the OpenCms server
      if (LOG.isErrorEnabled()) {
        LOG.error(e.getLocalizedMessage(getLocale()), e);
      }
      setException(e);
      return;
    }
    /// copied
    CmsConfigurationException exception = null;
    CmsModule module = null;
    try {
      String importpath = OpenCms.getSystemInfo().getPackagesRfsPath();
      importpath =
          OpenCms.getSystemInfo()
              .getAbsoluteRfsPathRelativeToWebInf(importpath + "modules/" + getParamImportfile());
      module = CmsModuleImportExportHandler.readModuleFromImport(importpath);

      // check if all dependencies are fulfilled
      List dependencies =
          OpenCms.getModuleManager()
              .checkDependencies(module, CmsModuleManager.DEPENDENCY_MODE_IMPORT);
      if (!dependencies.isEmpty()) {
        StringBuffer dep = new StringBuffer(32);
        for (int i = 0; i < dependencies.size(); i++) {
          CmsModuleDependency dependency = (CmsModuleDependency) dependencies.get(i);
          dep.append("\n - ");
          dep.append(dependency.getName());
          dep.append(" (Version: ");
          dep.append(dependency.getVersion());
          dep.append(")");
        }
        exception =
            new CmsConfigurationException(
                Messages.get()
                    .container(
                        Messages.ERR_ACTION_MODULE_DEPENDENCY_2,
                        getParamImportfile(),
                        new String(dep)));
      }
    } catch (CmsConfigurationException e) {
      exception = e;
    }

    if ((module != null) && (exception == null)) {

      // refresh the list
      Map objects = (Map) getSettings().getListObject();
      if (objects != null) {
        objects.remove(CmsModulesList.class.getName());
      }

      // redirect
      Map param = new HashMap();
      param.put(CmsModulesList.PARAM_MODULE, getParamImportfile());
      param.put(PARAM_STYLE, CmsToolDialog.STYLE_NEW);
      param.put(PARAM_CLOSELINK, CmsToolManager.linkForToolPath(getJsp(), "/modules"));
      if (OpenCms.getModuleManager().hasModule(module.getName())) {
        param.put(CmsModulesUploadFromServer.PARAM_MODULENAME, module.getName());
        getToolManager()
            .jspForwardPage(this, CmsModulesUploadFromServer.REPLACE_ACTION_REPORT, param);
      } else {
        getToolManager()
            .jspForwardPage(this, CmsModulesUploadFromServer.IMPORT_ACTION_REPORT, param);
      }
    } else {
      if (exception != null) {
        // log it
        if (LOG.isErrorEnabled()) {
          LOG.error(exception.getLocalizedMessage(getLocale()), exception);
        }
        // then throw to avoid blank page telling nothing due to missing forward
        throw new CmsRuntimeException(exception.getMessageContainer(), exception);
      }
    }
  }

  /** @see org.opencms.workplace.administration.A_CmsImportFromHttp#getDialogReturnUri() */
  public String getDialogReturnUri() {

    return DIALOG_URI;
  }

  /** @see org.opencms.workplace.administration.A_CmsImportFromHttp#getImportMessage() */
  public String getImportMessage() {

    return key(Messages.GUI_MODULES_IMPORT_FILE_0);
  }

  /** @see org.opencms.workplace.administration.A_CmsImportFromHttp#getStarttext() */
  public String getStarttext() {

    return key(Messages.GUI_MODULES_IMPORT_BLOCK_0);
  }

  /** @see org.opencms.workplace.CmsWorkplace#initMessages() */
  protected void initMessages() {

    // add specific dialog resource bundle
    addMessages(Messages.get().getBundleName());
    // add default resource bundles
    addMessages(org.opencms.workplace.Messages.get().getBundleName());
    addMessages(org.opencms.workplace.tools.Messages.get().getBundleName());
  }
}