/**
   * Prüfe, ob eine Ressource für ein bestimmten Appointment reserviert ist.
   *
   * @param alloc
   * @param when
   * @return <code>true</code>, wenn die Ressource reserviert ist. <code>false</code> sonst
   */
  private boolean isReserved(Allocatable alloc, Appointment when) {
    Reservation reservation = when.getReservation();
    Appointment[] restrictions = reservation.getRestriction(alloc);

    for (int restIt = 0, restLen = restrictions.length; restIt < restLen; restIt++) {
      if (when.equals(restrictions[restIt])) return true;
    }

    return (restrictions.length == 0);
  }
  /**
   * add attenddees if system property ical4j.validation.relaxed is set ony
   *
   * @param appointment
   * @param properties
   */
  private void addAttendees(
      Appointment appointment,
      PropertyList properties,
      boolean doExportAsMeeting,
      String exportAttendeesParticipationStatus) {
    if (!doExportAsMeeting) return;

    Allocatable[] persons = appointment.getReservation().getAllocatablesFor(appointment);

    for (Allocatable person : persons) {
      if (!person.isPerson()) {
        continue;
      }
      String email = null;
      Attribute attr = person.getClassification().getAttribute(exportAttendeesAttribute);
      if (attr != null && person.getClassification().getValue(attr) != null)
        email = person.getClassification().getValue(attr).toString().trim();
      // determine if person has email attribute
      if (email != null && email.length() > 0) {
        try {
          Attendee attendee = new Attendee(new URI(email));
          attendee.getParameters().add(Role.REQ_PARTICIPANT);
          attendee.getParameters().add(new Cn(person.getName(raplaLocale.getLocale())));
          attendee.getParameters().add(new PartStat(exportAttendeesParticipationStatus));
          properties.add(attendee);
        } catch (URISyntaxException e) {
          throw new IllegalArgumentException(e);
        }
      }
    }
  }
 /**
  * add organizer to properties
  *
  * @param appointment
  * @param properties
  */
 private void addOrganizer(
     Appointment appointment, PropertyList properties, boolean doExportAsMeeting) {
   // means we do not export attendees so we do not have a meeting
   if (!doExportAsMeeting) return;
   final User owner = appointment.getReservation().getOwner();
   try {
     Organizer organizer = null;
     if (owner.getEmail() != null && owner.getEmail().trim().length() > 0) {
       try {
         final URI uri = new URI("MAILTO:" + owner.getEmail().trim());
         organizer = new Organizer(uri);
       } catch (URISyntaxException e) {
       }
     }
     if (organizer == null) {
       organizer = new Organizer("MAILTO:" + URLEncoder.encode(owner.getUsername(), "UTF-8"));
     }
     if (!"".equals(owner.getName())) organizer.getParameters().add(new Cn(owner.getName()));
     properties.add(organizer);
   } catch (URISyntaxException e) {
     throw new IllegalArgumentException(e);
   } catch (UnsupportedEncodingException e) {
     throw new IllegalArgumentException(e);
   }
 }
  /**
   * Erstellt anhand des &uuml;bergebenen Appointment-Objekts einen iCalendar-Event.
   *
   * @param appointment Ein Rapla Appointment.
   * @return Ein iCalendar-Event mit den Daten des Appointments.
   */
  private VEvent createVEvent(
      Appointment appointment,
      boolean doExportAsMeeting,
      String exportAttendeesParticipationStatus) {

    PropertyList properties = new PropertyList();

    boolean isAllDayEvent = appointment.isWholeDaysSet();
    addDateStampToEvent(appointment, properties);
    addCreateDateToEvent(appointment, properties);
    addStartDateToEvent(appointment, properties, isAllDayEvent);
    addLastModifiedDateToEvent(appointment, properties);
    addEndDateToEvent(appointment, properties, isAllDayEvent);
    addEventNameToEvent(appointment, properties);
    addDescriptionToEvent(appointment, properties);
    addUidToEvent(appointment, properties);
    addLocationToEvent(appointment, properties);
    addCategories(appointment, properties);
    addOrganizer(appointment, properties, doExportAsMeeting);
    addAttendees(appointment, properties, doExportAsMeeting, exportAttendeesParticipationStatus);
    addRepeatings(appointment, properties);

    VEvent event = new VEvent(properties);

    return event;
  }
  /**
   * F&uuml;gt einem iCal-Event das Startdatum aus dem &uuml;bergebenen Appointment-Objekt hinzu.
   *
   * @param appointment
   */
  private void addStartDateToEvent(
      Appointment appointment, PropertyList properties, boolean isAllDayEvent) {

    Date startDate = appointment.getStart();

    if (isAllDayEvent) {
      DtStart start = getDtStartFromAllDayEvent(startDate, calendar);
      properties.add(start);
    } else {
      if (appointment.getRepeating() == null) {
        DtStart start = new DtStart(convertRaplaLocaleToUTC(startDate));
        start.setUtc(true);
        properties.add(start);
      } else {
        DtStart start = getStartDateProperty(startDate);
        properties.add(start);
      }
    }
  }
  /**
   * F&uuml;gt einem iCal-Event das Enddatum aus dem &uuml;bergebenen Appointment-Objekt hinzu.
   *
   * @param appointment
   */
  private void addEndDateToEvent(
      Appointment appointment, PropertyList properties, boolean isAllDayEvent) {

    Date endDate = appointment.getEnd();
    java.util.Calendar calendar = java.util.Calendar.getInstance();

    if (isAllDayEvent) {
      DtEnd end = getDtEndFromAllDayEvent(endDate, calendar);
      properties.add(end);
    } else {
      if (appointment.getRepeating() == null) {
        DtEnd end = new DtEnd(convertRaplaLocaleToUTC(endDate));
        end.setUtc(true);
        properties.add(end);
      } else {
        DtEnd end = getEndDateProperty(endDate);
        properties.add(end);
      }
    }
  }
 /**
  * F&uuml;gt einem iCal-Event den Termin-Namen aus dem &uuml;bergebenen Appointment-Objekt hinzu.
  *
  * @param appointment
  */
 private void addEventNameToEvent(Appointment appointment, PropertyList properties) {
   Reservation reservation = appointment.getReservation();
   final Locale locale = raplaLocale.getLocale();
   String eventDescription = NameFormatUtil.getExportName(appointment, locale);
   if (reservation
           .getClassification()
           .getType()
           .getAnnotation(DynamicTypeAnnotations.KEY_NAME_FORMAT_EXPORT)
       == null) {
     eventDescription += getAttendeeString(appointment);
   }
   properties.add(new Summary(eventDescription));
 }
  /**
   * F&uuml;gt einem iCal-Event den Ort aus dem &uuml;bergebenen Appointment-Objekt hinzu.
   *
   * @param appointment
   */
  private void addLocationToEvent(Appointment appointment, PropertyList properties) {
    Allocatable[] allocatables = appointment.getReservation().getAllocatablesFor(appointment);
    StringBuffer buffer = new StringBuffer();
    for (Allocatable alloc : allocatables) {
      if (hasLocationType) {
        if (alloc.getClassification().getType().getAnnotation(DynamicTypeAnnotations.KEY_LOCATION)
            == null) {
          continue;
        }
      } else if (alloc.isPerson()) {
        continue;
      }
      if (buffer.length() > 0) {
        buffer.append(", ");
      }
      buffer.append(alloc.getName(raplaLocale.getLocale()));
    }

    properties.add(new Location(buffer.toString()));
  }
  private void addDescriptionToEvent(Appointment appointment, PropertyList properties) {

    Reservation reservation = appointment.getReservation();
    String eventDescription;
    if (reservation
            .getClassification()
            .getType()
            .getAnnotation(DynamicTypeAnnotations.KEY_DESCRIPTION_FORMAT_EXPORT)
        != null) {
      eventDescription =
          reservation.format(
              raplaLocale.getLocale(),
              DynamicTypeAnnotations.KEY_DESCRIPTION_FORMAT_EXPORT,
              appointment);
    } else {
      eventDescription = null;
    }
    if (eventDescription != null) {
      properties.add(new Description(eventDescription));
    }
  }
  public String getAttendeeString(Appointment appointment) {

    String attendeeString = "";

    Reservation raplaReservation = appointment.getReservation();
    Allocatable[] raplaPersons = raplaReservation.getPersons();

    for (int i = 0; i < raplaPersons.length; i++) {
      if (!isReserved(raplaPersons[i], appointment)) continue;

      attendeeString += raplaPersons[i].getName(raplaLocale.getLocale());
      attendeeString = attendeeString.trim();

      if (i != raplaPersons.length - 1) {
        attendeeString += ", ";
      } else {
        attendeeString = " [" + attendeeString + "]";
      }
    }

    return attendeeString;
  }
  /**
   * F&uuml;gt einem iCal-Event das Erstelldatum aus dem &uuml;bergebenen Appointment-Objekt hinzu.
   *
   * @param appointment
   */
  private void addCreateDateToEvent(Appointment appointment, PropertyList properties) {

    Date createTime = appointment.getReservation().getCreateTime();
    properties.add(new Created(convertRaplaLocaleToUTC(createTime)));
  }
 /**
  * F&uuml;gt die Kategorien hinzu.
  *
  * @param appointment Ein Rapla Appointment.
  */
 private void addCategories(Appointment appointment, PropertyList properties) {
   Classification cls = appointment.getReservation().getClassification();
   Categories cat = new Categories();
   cat.getCategories().add(cls.getType().getName(raplaLocale.getLocale()));
   properties.add(cat);
 }
  /**
   * F&uuml;gt die Wiederholungen des &uuml;bergebenen Appointment-Objekts dem &uuml;bergebenen
   * Event-Objekt hinzu.
   *
   * @param appointment Ein Rapla Appointment.
   */
  private void addRepeatings(Appointment appointment, PropertyList properties) {
    Repeating repeating = appointment.getRepeating();

    if (repeating == null) {
      return;
    }

    // This returns the strings DAYLY, WEEKLY, MONTHLY, YEARLY
    String type = repeating.getType().toString().toUpperCase();

    Recur recur;

    // here is evaluated, if a COUNT is set in Rapla, or an enddate is
    // specified
    if (repeating.getNumber() == -1) {
      recur = new Recur(type, -1);
    } else if (repeating.isFixedNumber()) {
      recur = new Recur(type, repeating.getNumber());
    } else {
      net.fortuna.ical4j.model.Date endDate = new net.fortuna.ical4j.model.Date(repeating.getEnd());
      // TODO do we need to translate the enddate in utc?
      recur = new Recur(type, endDate);
    }

    if (repeating.isDaily()) {
      // DAYLY -> settings : intervall
      recur.setInterval(repeating.getInterval());

    } else if (repeating.isWeekly()) {
      // WEEKLY -> settings : every nTh Weekday
      recur.setInterval(repeating.getInterval());

      calendar.setTime(appointment.getStart());
      recur.getDayList().add(WeekDay.getWeekDay(calendar));
    } else if (repeating.isMonthly()) {
      // MONTHLY -> settings : every nTh Weekday
      recur.setInterval(repeating.getInterval());
      calendar.setTime(appointment.getStart());
      int weekofmonth =
          Math.round(calendar.get(java.util.Calendar.DAY_OF_MONTH) / DateTools.DAYS_PER_WEEK) + 1;
      recur.getDayList().add(new WeekDay(WeekDay.getWeekDay(calendar), weekofmonth));
    } else if (repeating.isYearly()) {
      // YEARLY -> settings : every nTh day mTh Monthname
      calendar.setTime(appointment.getStart());
      calendar.get(java.util.Calendar.DAY_OF_YEAR);
    } else {
      getLogger().warn("Invalid data in recurrency rule!");
    }

    properties.add(new RRule(recur));

    // bugfix - if rapla has no exceptions, an empty EXDATE: element is
    // produced. This may bother some iCal tools
    if (repeating.getExceptions().length == 0) {
      return;
    }

    // Add exception dates
    // DateList dl = new DateList(Value.DATE);

    ExDate exDate = new ExDate();
    TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
    net.fortuna.ical4j.model.TimeZone tz = registry.getTimeZone(timeZone.getID());

    // rku: use seperate EXDATE for each exception
    for (Iterator<Date> itExceptions = Arrays.asList(repeating.getExceptions()).iterator();
        itExceptions.hasNext(); ) {
      // DateList dl = new DateList(Value.DATE);
      Date date = itExceptions.next();
      // dl.add(new net.fortuna.ical4j.model.Date( date));
      java.util.Calendar cal = raplaLocale.createCalendar();
      cal.setTime(date);
      int year = cal.get(java.util.Calendar.YEAR);
      int day_of_year = cal.get(java.util.Calendar.DAY_OF_YEAR);
      cal.setTime(appointment.getStart());
      cal.set(java.util.Calendar.YEAR, year);
      cal.set(java.util.Calendar.DAY_OF_YEAR, day_of_year);
      int offset =
          (int) (tz.getOffset(DateTools.cutDate(date).getTime()) / DateTools.MILLISECONDS_PER_HOUR);
      cal.add(java.util.Calendar.HOUR, -offset);
      Date dateToSave = cal.getTime();
      net.fortuna.ical4j.model.DateTime dateTime = new net.fortuna.ical4j.model.DateTime();
      dateTime.setTime(dateToSave.getTime());
      exDate.getDates().add(dateTime);
    }
    exDate.setTimeZone(tz);

    properties.add(exDate);
    // properties.add(new ExDate(dl));
  }
 /**
  * Fuegt dem Termin den DTSTAMP hinzu lt. rfc muss dies dem Erstellungdatum des Objektes
  * entsprechen. Da dies jedoch den Import erschwert und sinnlos ist, wird es auf das letzte
  * Modifizierungsdatum geschrieben.
  */
 private void addLastModifiedDateToEvent(Appointment appointment, PropertyList properties) {
   Date lastChange = appointment.getReservation().getLastChanged();
   properties.add(new DtStamp(convertRaplaLocaleToUTC(lastChange)));
 }