/**
   * Process the start of this tag.
   *
   * @return int status
   * @exception JspException if a JSP exception has occurred
   * @see javax.servlet.jsp.tagext.Tag#doStartTag()
   */
  public int doStartTag() throws JspException {
    ExpressionEvaluator eval = new ExpressionEvaluator(this, pageContext);

    if (selected != null) {
      selected = eval.evalString("default", selected);
    }

    Locale userLocale = pageContext.getRequest().getLocale();
    List countries = this.buildCountryList(userLocale);

    if (scope != null) {
      if (scope.equals("page")) {
        pageContext.setAttribute(name, countries);
      } else if (scope.equals("request")) {
        pageContext.getRequest().setAttribute(name, countries);
      } else if (scope.equals("session")) {
        pageContext.getSession().setAttribute(name, countries);
      } else if (scope.equals("application")) {
        pageContext.getServletContext().setAttribute(name, countries);
      } else {
        throw new JspException("Attribute 'scope' must be: page, request, session or application");
      }
    } else {
      StringBuffer sb = new StringBuffer();
      sb.append("<select name=\"" + name + "\" id=\"" + name + "\" class=\"form-control\">\n");

      if (prompt != null) {
        sb.append("    <option value=\"\" selected=\"selected\">");
        sb.append(eval.evalString("prompt", prompt) + "</option>\n");
      }

      for (Iterator i = countries.iterator(); i.hasNext(); ) {
        LabelValue country = (LabelValue) i.next();
        sb.append("    <option value=\"" + country.getValue() + "\"");

        if ((selected != null) && selected.equals(country.getValue())) {
          sb.append(" selected=\"selected\"");
        }

        sb.append(">" + country.getLabel() + "</option>\n");
      }

      sb.append("</select>");

      try {
        pageContext.getOut().write(sb.toString());
      } catch (IOException io) {
        throw new JspException(io);
      }
    }

    return super.doStartTag();
  }
  /**
   * Build a List of LabelValues for all the available countries. Uses the two letter uppercase ISO
   * name of the country as the value and the localized country name as the label.
   *
   * @param locale The Locale used to localize the country names.
   * @return List of LabelValues for all available countries.
   */
  @SuppressWarnings("unchecked")
  public Map<String, String> getCountries(Locale locale) {
    if (availableCountries == null) {
      final String EMPTY = "";
      final Locale[] available = Locale.getAvailableLocales();

      List<LabelValue> countries = new ArrayList<LabelValue>();
      countries.add(new LabelValue("", ""));

      for (Locale anAvailable : available) {
        final String iso = anAvailable.getCountry();
        final String name = anAvailable.getDisplayCountry(locale);

        if (!EMPTY.equals(iso) && !EMPTY.equals(name)) {
          LabelValue country = new LabelValue(name, iso);

          if (!countries.contains(country)) {
            countries.add(new LabelValue(name, iso));
          }
        }
      }

      Collections.sort(countries, new LabelValueComparator(locale));

      Map<String, String> options = new LinkedHashMap<String, String>();
      // loop through and convert list to a JSF-Friendly Map for a <select>
      for (Object country : countries) {
        LabelValue option = (LabelValue) country;
        if (!options.containsValue(option.getValue())) {
          options.put(option.getLabel(), option.getValue());
        }
      }
      this.availableCountries = options;
    }

    return availableCountries;
  }
    /**
     * Compares the localized labels of two LabelValues.
     *
     * @param o1 The first LabelValue to compare.
     * @param o2 The second LabelValue to compare.
     * @return The value returned by comparing the localized labels.
     */
    public final int compare(Object o1, Object o2) {
      LabelValue lhs = (LabelValue) o1;
      LabelValue rhs = (LabelValue) o2;

      return c.compare(lhs.getLabel(), rhs.getLabel());
    }