/* (non-Javadoc)
  * @see org.eclipse.team.core.synchronize.FastSyncInfoFilter#select(org.eclipse.team.core.synchronize.SyncInfo)
  */
 public boolean select(SyncInfo info) {
   int syncKind = info.getKind();
   for (int i = 0; i < directionFilters.length; i++) {
     int filter = directionFilters[i];
     if ((syncKind & SyncInfo.DIRECTION_MASK) == filter) return true;
   }
   return false;
 }
 /* (non-Javadoc)
  * @see org.eclipse.team.core.synchronize.FastSyncInfoFilter#select(org.eclipse.team.core.synchronize.SyncInfo)
  */
 public boolean select(SyncInfo info) {
   int syncKind = info.getKind();
   for (int i = 0; i < changeFilters.length; i++) {
     int filter = changeFilters[i];
     if ((syncKind & SyncInfo.CHANGE_MASK) == filter) return true;
   }
   return false;
 }
  public void updateProvider(
      Feed feed, Long syncLocalId, Entry entry, ContentProvider provider, Object info)
      throws ParseException {
    SyncInfo syncInfo = (SyncInfo) info;
    EventEntry event = (EventEntry) entry;

    ContentValues map = new ContentValues();

    // use the calendar's timezone, if provided in the feed.
    // this overwrites whatever was in the db.
    if ((feed != null) && (feed instanceof EventsFeed)) {
      EventsFeed eventsFeed = (EventsFeed) feed;
      syncInfo.calendarTimezone = eventsFeed.getTimezone();
    }

    if (entry.isDeleted()) {
      deletedEntryToContentValues(syncLocalId, event, map);
      if (Config.LOGV) {
        Log.v(TAG, "Deleting entry: " + map);
      }
      provider.insert(Events.DELETED_CONTENT_URI, map);
      return;
    }

    int entryState = entryToContentValues(event, syncLocalId, map, syncInfo);

    if (entryState == ENTRY_DELETED) {
      if (Config.LOGV) {
        Log.v(TAG, "Got deleted entry from server: " + map);
      }
      provider.insert(Events.DELETED_CONTENT_URI, map);
    } else if (entryState == ENTRY_OK) {
      if (Config.LOGV) {
        Log.v(TAG, "Got entry from server: " + map);
      }
      Uri result = provider.insert(Events.CONTENT_URI, map);
      long rowId = ContentUris.parseId(result);
      // handle the reminders for the event
      Integer hasAlarm = map.getAsInteger(Events.HAS_ALARM);
      if (hasAlarm != null && hasAlarm == 1) {
        // reminders should not be null
        Vector alarms = event.getReminders();
        if (alarms == null) {
          Log.e(TAG, "Have an alarm but do not have any reminders " + "-- should not happen.");
          throw new IllegalStateException("Have an alarm but do not have any reminders");
        }
        Enumeration reminders = alarms.elements();
        while (reminders.hasMoreElements()) {
          ContentValues reminderValues = new ContentValues();
          reminderValues.put(Calendar.Reminders.EVENT_ID, rowId);

          Reminder reminder = (Reminder) reminders.nextElement();
          byte method = reminder.getMethod();
          switch (method) {
            case Reminder.METHOD_DEFAULT:
              reminderValues.put(Calendar.Reminders.METHOD, Calendar.Reminders.METHOD_DEFAULT);
              break;
            case Reminder.METHOD_ALERT:
              reminderValues.put(Calendar.Reminders.METHOD, Calendar.Reminders.METHOD_ALERT);
              break;
            case Reminder.METHOD_EMAIL:
              reminderValues.put(Calendar.Reminders.METHOD, Calendar.Reminders.METHOD_EMAIL);
              break;
            case Reminder.METHOD_SMS:
              reminderValues.put(Calendar.Reminders.METHOD, Calendar.Reminders.METHOD_SMS);
              break;
            default:
              // should not happen.  return false?  we'd have to
              // roll back the event.
              Log.e(TAG, "Unknown reminder method: " + method + " should not happen!");
          }

          int minutes = reminder.getMinutes();
          reminderValues.put(
              Calendar.Reminders.MINUTES,
              minutes == Reminder.MINUTES_DEFAULT ? Calendar.Reminders.MINUTES_DEFAULT : minutes);

          if (provider.insert(Calendar.Reminders.CONTENT_URI, reminderValues) == null) {
            throw new ParseException("Unable to insert reminders.");
          }
        }
      }

      // handle attendees for the event
      Vector attendees = event.getAttendees();
      Enumeration attendeesEnum = attendees.elements();
      while (attendeesEnum.hasMoreElements()) {
        Who who = (Who) attendeesEnum.nextElement();
        ContentValues attendeesValues = new ContentValues();
        attendeesValues.put(Calendar.Attendees.EVENT_ID, rowId);
        attendeesValues.put(Calendar.Attendees.ATTENDEE_NAME, who.getValue());
        attendeesValues.put(Calendar.Attendees.ATTENDEE_EMAIL, who.getEmail());

        byte status;
        switch (who.getStatus()) {
          case Who.STATUS_NONE:
            status = Calendar.Attendees.ATTENDEE_STATUS_NONE;
            break;
          case Who.STATUS_INVITED:
            status = Calendar.Attendees.ATTENDEE_STATUS_INVITED;
            break;
          case Who.STATUS_ACCEPTED:
            status = Calendar.Attendees.ATTENDEE_STATUS_ACCEPTED;
            break;
          case Who.STATUS_TENTATIVE:
            status = Calendar.Attendees.ATTENDEE_STATUS_TENTATIVE;
            break;
          case Who.STATUS_DECLINED:
            status = Calendar.Attendees.ATTENDEE_STATUS_DECLINED;
            break;
          default:
            Log.w(TAG, "Unknown attendee status " + who.getStatus());
            status = Calendar.Attendees.ATTENDEE_STATUS_NONE;
        }
        attendeesValues.put(Calendar.Attendees.ATTENDEE_STATUS, status);
        byte rel;
        switch (who.getRelationship()) {
          case Who.RELATIONSHIP_NONE:
            rel = Calendar.Attendees.RELATIONSHIP_NONE;
            break;
          case Who.RELATIONSHIP_ORGANIZER:
            rel = Calendar.Attendees.RELATIONSHIP_ORGANIZER;
            break;
          case Who.RELATIONSHIP_ATTENDEE:
            rel = Calendar.Attendees.RELATIONSHIP_ATTENDEE;
            break;
          case Who.RELATIONSHIP_PERFORMER:
            rel = Calendar.Attendees.RELATIONSHIP_PERFORMER;
            break;
          case Who.RELATIONSHIP_SPEAKER:
            rel = Calendar.Attendees.RELATIONSHIP_SPEAKER;
            break;
          default:
            Log.w(TAG, "Unknown attendee relationship " + who.getRelationship());
            rel = Calendar.Attendees.RELATIONSHIP_NONE;
        }

        attendeesValues.put(Calendar.Attendees.ATTENDEE_RELATIONSHIP, rel);

        byte type;
        switch (who.getType()) {
          case Who.TYPE_NONE:
            type = Calendar.Attendees.TYPE_NONE;
            break;
          case Who.TYPE_REQUIRED:
            type = Calendar.Attendees.TYPE_REQUIRED;
            break;
          case Who.TYPE_OPTIONAL:
            type = Calendar.Attendees.TYPE_OPTIONAL;
            break;
          default:
            Log.w(TAG, "Unknown attendee type " + who.getType());
            type = Calendar.Attendees.TYPE_NONE;
        }
        attendeesValues.put(Calendar.Attendees.ATTENDEE_TYPE, type);
        if (provider.insert(Calendar.Attendees.CONTENT_URI, attendeesValues) == null) {
          throw new ParseException("Unable to insert attendees.");
        }
      }

      // handle the extended properties for the event
      Integer hasExtendedProperties = map.getAsInteger(Events.HAS_EXTENDED_PROPERTIES);
      if (hasExtendedProperties != null && hasExtendedProperties.intValue() != 0) {
        // extended properties should not be null
        // TODO: make the extended properties a bit more OO?
        Hashtable extendedProperties = event.getExtendedProperties();
        if (extendedProperties == null) {
          Log.e(
              TAG,
              "Have extendedProperties but do not have any properties" + "-- should not happen.");
          throw new IllegalStateException("Have extendedProperties but do not have any properties");
        }
        Enumeration propertyNames = extendedProperties.keys();
        while (propertyNames.hasMoreElements()) {
          String propertyName = (String) propertyNames.nextElement();
          String propertyValue = (String) extendedProperties.get(propertyName);
          ContentValues extendedPropertyValues = new ContentValues();
          extendedPropertyValues.put(Calendar.ExtendedProperties.EVENT_ID, rowId);
          extendedPropertyValues.put(Calendar.ExtendedProperties.NAME, propertyName);
          extendedPropertyValues.put(Calendar.ExtendedProperties.VALUE, propertyValue);
          if (provider.insert(Calendar.ExtendedProperties.CONTENT_URI, extendedPropertyValues)
              == null) {
            throw new ParseException("Unable to insert extended properties.");
          }
        }
      }
    } else {
      // If the DTSTART == -1, then the date was out of range.  We don't
      // need to throw a ParseException because the user can create
      // dates on the web that we can't handle on the phone.  For
      // example, events with dates before Dec 13, 1901 can be created
      // on the web but cannot be handled on the phone.
      Long dtstart = map.getAsLong(Events.DTSTART);
      if (dtstart != null && dtstart == -1) {
        return;
      }

      if (Config.LOGV) {
        Log.v(TAG, "Got invalid entry from server: " + map);
      }
      throw new ParseException("Got invalid entry from server: " + map);
    }
  }
  @Override
  protected String cursorToEntry(SyncContext context, Cursor c, Entry entry, Object info)
      throws ParseException {
    EventEntry event = (EventEntry) entry;
    SyncInfo syncInfo = (SyncInfo) info;

    String feedUrl = c.getString(c.getColumnIndex(Calendars.URL));

    // update the sync info.  this will be used later when we update the
    // provider with the results of sending this entry to the calendar
    // server.
    syncInfo.calendarId = c.getLong(c.getColumnIndex(Events.CALENDAR_ID));
    syncInfo.calendarTimezone = c.getString(c.getColumnIndex(Events.EVENT_TIMEZONE));
    if (TextUtils.isEmpty(syncInfo.calendarTimezone)) {
      // if the event timezone is not set -- e.g., when we're creating an
      // event on the device -- we will use the timezone for the calendar.
      syncInfo.calendarTimezone = c.getString(c.getColumnIndex(Events.TIMEZONE));
    }

    // id
    event.setId(c.getString(c.getColumnIndex(Events._SYNC_ID)));
    event.setEditUri(c.getString(c.getColumnIndex(Events._SYNC_VERSION)));

    // status
    byte status;
    int localStatus = c.getInt(c.getColumnIndex(Events.STATUS));
    switch (localStatus) {
      case Events.STATUS_CANCELED:
        status = EventEntry.STATUS_CANCELED;
        break;
      case Events.STATUS_CONFIRMED:
        status = EventEntry.STATUS_CONFIRMED;
        break;
      case Events.STATUS_TENTATIVE:
        status = EventEntry.STATUS_TENTATIVE;
        break;
      default:
        // should not happen
        status = EventEntry.STATUS_TENTATIVE;
        break;
    }
    event.setStatus(status);

    // visibility
    byte visibility;
    int localVisibility = c.getInt(c.getColumnIndex(Events.VISIBILITY));
    switch (localVisibility) {
      case Events.VISIBILITY_DEFAULT:
        visibility = EventEntry.VISIBILITY_DEFAULT;
        break;
      case Events.VISIBILITY_CONFIDENTIAL:
        visibility = EventEntry.VISIBILITY_CONFIDENTIAL;
        break;
      case Events.VISIBILITY_PRIVATE:
        visibility = EventEntry.VISIBILITY_PRIVATE;
        break;
      case Events.VISIBILITY_PUBLIC:
        visibility = EventEntry.VISIBILITY_PUBLIC;
        break;
      default:
        // should not happen
        Log.e(
            TAG,
            "Unexpected value for visibility: " + localVisibility + "; using default visibility.");
        visibility = EventEntry.VISIBILITY_DEFAULT;
        break;
    }
    event.setVisibility(visibility);

    byte transparency;
    int localTransparency = c.getInt(c.getColumnIndex(Events.TRANSPARENCY));
    switch (localTransparency) {
      case Events.TRANSPARENCY_OPAQUE:
        transparency = EventEntry.TRANSPARENCY_OPAQUE;
        break;
      case Events.TRANSPARENCY_TRANSPARENT:
        transparency = EventEntry.TRANSPARENCY_TRANSPARENT;
        break;
      default:
        // should not happen
        Log.e(
            TAG,
            "Unexpected value for transparency: "
                + localTransparency
                + "; using opaque transparency.");
        transparency = EventEntry.TRANSPARENCY_OPAQUE;
        break;
    }
    event.setTransparency(transparency);

    // could set the html uri, but there's no need to, since it should not be edited.

    // title
    event.setTitle(c.getString(c.getColumnIndex(Events.TITLE)));

    // description
    event.setContent(c.getString(c.getColumnIndex(Events.DESCRIPTION)));

    // where
    event.setWhere(c.getString(c.getColumnIndex(Events.EVENT_LOCATION)));

    // attendees
    long eventId = c.getInt(c.getColumnIndex(Events._SYNC_LOCAL_ID));
    addAttendeesToEntry(eventId, event);

    // comment uri
    event.setCommentsUri(c.getString(c.getColumnIndexOrThrow(Events.COMMENTS_URI)));

    Time utc = new Time(Time.TIMEZONE_UTC);

    boolean allDay = c.getInt(c.getColumnIndex(Events.ALL_DAY)) != 0;

    String startTime = null;
    String endTime = null;
    // start time
    int dtstartColumn = c.getColumnIndex(Events.DTSTART);
    if (!c.isNull(dtstartColumn)) {
      long dtstart = c.getLong(dtstartColumn);
      utc.set(dtstart);
      startTime = utc.format3339(allDay);
    }

    // end time
    int dtendColumn = c.getColumnIndex(Events.DTEND);
    if (!c.isNull(dtendColumn)) {
      long dtend = c.getLong(dtendColumn);
      utc.set(dtend);
      endTime = utc.format3339(allDay);
    }

    When when = new When(startTime, endTime);
    event.addWhen(when);

    // reminders
    Integer hasReminder = c.getInt(c.getColumnIndex(Events.HAS_ALARM));
    if (hasReminder != null && hasReminder.intValue() != 0) {
      addRemindersToEntry(eventId, event);
    }

    // extendedProperties
    Integer hasExtendedProperties = c.getInt(c.getColumnIndex(Events.HAS_EXTENDED_PROPERTIES));
    if (hasExtendedProperties != null && hasExtendedProperties.intValue() != 0) {
      addExtendedPropertiesToEntry(eventId, event);
    }

    long originalStartTime = -1;
    String originalId = c.getString(c.getColumnIndex(Events.ORIGINAL_EVENT));
    int originalStartTimeIndex = c.getColumnIndex(Events.ORIGINAL_INSTANCE_TIME);
    if (!c.isNull(originalStartTimeIndex)) {
      originalStartTime = c.getLong(originalStartTimeIndex);
    }
    if ((originalStartTime != -1) && !TextUtils.isEmpty(originalId)) {
      // We need to use the "originalAllDay" field for the original event
      // in order to format the "originalStartTime" correctly.
      boolean originalAllDay = c.getInt(c.getColumnIndex(Events.ORIGINAL_ALL_DAY)) != 0;

      Time originalTime = new Time(c.getString(c.getColumnIndex(Events.EVENT_TIMEZONE)));
      originalTime.set(originalStartTime);

      utc.set(originalStartTime);
      event.setOriginalEventStartTime(utc.format3339(originalAllDay));
      event.setOriginalEventId(originalId);
    }

    // recurrences.
    ICalendar.Component component = new ICalendar.Component("DUMMY", null /* parent */);
    if (RecurrenceSet.populateComponent(c, component)) {
      addRecurrenceToEntry(component, event);
    }

    // if this is a new entry, return the feed url.  otherwise, return null; the edit url is
    // already in the entry.
    if (event.getEditUri() == null) {
      return feedUrl;
    } else {
      return null;
    }
  }
  private void getServerDiffsForFeed(
      SyncContext context,
      SyncData baseSyncData,
      SyncableContentProvider tempProvider,
      String feed,
      Object baseSyncInfo,
      SyncResult syncResult) {
    final SyncInfo syncInfo = (SyncInfo) baseSyncInfo;
    final GDataSyncData syncData = (GDataSyncData) baseSyncData;

    Cursor cursor =
        getContext()
            .getContentResolver()
            .query(
                Calendar.Calendars.CONTENT_URI,
                CALENDARS_PROJECTION,
                SELECT_BY_ACCOUNT_AND_FEED,
                new String[] {getAccount(), feed},
                null /* sort order */);

    ContentValues map = new ContentValues();
    int maxResults = getMaxEntriesPerSync();

    try {
      if (!cursor.moveToFirst()) {
        return;
      }
      // TODO: refactor all of this, so we don't have to rely on
      // member variables getting updated here in order for the
      // base class hooks to work.

      syncInfo.calendarId = cursor.getLong(0);
      boolean syncEvents = (cursor.getInt(6) == 1);
      long syncTime = cursor.getLong(2);
      String feedUrl = cursor.getString(3);
      String name = cursor.getString(4);
      String origCalendarTimezone = syncInfo.calendarTimezone = cursor.getString(5);

      if (!syncEvents) {
        // should not happen.  non-syncable feeds should not be scheduled for syncs nor
        // should they get tickled.
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
          Log.v(TAG, "Ignoring sync request for non-syncable feed.");
        }
        return;
      }

      context.setStatusText("Syncing " + name);

      // call the superclass implementation to sync the current
      // calendar from the server.
      getServerDiffsImpl(
          context,
          tempProvider,
          getFeedEntryClass(),
          feedUrl,
          syncInfo,
          maxResults,
          syncData,
          syncResult);
      if (mSyncCanceled || syncResult.hasError()) {
        return;
      }

      // update the timezone for this calendar if it changed
      if (!TextUtils.equals(syncInfo.calendarTimezone, origCalendarTimezone)) {
        map.clear();
        map.put(Calendars.TIMEZONE, syncInfo.calendarTimezone);
        mContentResolver.update(
            ContentUris.withAppendedId(Calendars.CONTENT_URI, syncInfo.calendarId),
            map,
            null,
            null);
      }
    } finally {
      cursor.close();
    }
  }
 /* (non-Javadoc)
  * @see org.eclipse.team.core.synchronize.FastSyncInfoFilter#select(org.eclipse.team.core.synchronize.SyncInfo)
  */
 public boolean select(SyncInfo info) {
   return info.getKind() != 0 && (info.getKind() & SyncInfo.PSEUDO_CONFLICT) == 0;
 }
 /* (non-Javadoc)
  * @see org.eclipse.team.core.synchronize.FastSyncInfoFilter#select(org.eclipse.team.core.synchronize.SyncInfo)
  */
 public boolean select(SyncInfo info) {
   return (info.getKind() & SyncInfo.AUTOMERGE_CONFLICT) != 0;
 }
 /**
  * Return whether the provided <code>SyncInfo</code> matches the filter. The default behavior it
  * to include resources whose syncKind is non-zero.
  *
  * @param info the <code>SyncInfo</code> being tested
  * @return <code>true</code> if the <code>SyncInfo</code> matches the filter
  */
 public boolean select(SyncInfo info) {
   return info.getKind() != 0;
 }