// We'll need this later.
public static GetEntityResponse<EventInfo> toEvent(
          final IcalCallback cb,
          final BwCalendar cal,
          final Icalendar ical,
          final Component val,
          final boolean mergeAttendees) {
    final var resp = new GetEntityResponse<EventInfo>();
    if (val == null) {
      return Response.notOk(resp, failed, "No component supplied");
    }
    String currentPrincipal = null;
    final BwPrincipal principal = cb.getPrincipal();
    if (principal != null) {
      currentPrincipal = principal.getPrincipalRef();
    }
    final Holder<Boolean> hasXparams = new Holder<>(Boolean.FALSE);
    final int methodType = ical.getMethodType();
    String attUri = null;
    if (mergeAttendees) {
      // We'll need this later.
      attUri = cb.getCaladdr(cb.getPrincipal().getPrincipalRef());
    }
    final String colPath;
    if (cal == null) {
      colPath = null;
    } else {
      colPath = cal.getPath();
    }
    try {
      final PropertyList<Property> pl = val.getProperties();
      boolean vpoll = false;
      boolean event = false;
      boolean task = false;
      if (pl == null) {
        // Empty component
        return Response.notOk(resp, failed, "Empty component");
      }
      final int entityType;
      if (val instanceof VEvent) {
        entityType = IcalDefs.entityTypeEvent;
        event = true;
      } else if (val instanceof VToDo) {
        entityType = IcalDefs.entityTypeTodo;
        task = true;
      } else if (val instanceof VJournal) {
        entityType = IcalDefs.entityTypeJournal;
      } else if (val instanceof VFreeBusy) {
        entityType = IcalDefs.entityTypeFreeAndBusy;
      } else if (val instanceof VAvailability) {
        entityType = IcalDefs.entityTypeVavailability;
      } else if (val instanceof Available) {
        entityType = IcalDefs.entityTypeAvailable;
      } else if (val instanceof VPoll) {
        entityType = IcalDefs.entityTypeVpoll;
        vpoll = true;
      } else {
        return Response.error(resp, "org.bedework.invalid.component.type: " +
                val.getName());
      }
      // Get the guid from the component
      String guid = null;
      final Uid uidp = pl.getProperty(Property.UID);
      if (uidp != null) {
        testXparams(uidp, hasXparams);
        guid = uidp.getValue();
      }
      if (guid == null) {
        /* XXX A guid is required - but are there devices out there without a
         *       guid - and if so how do we handle it?
         */
        return Response.notOk(resp, failed, CalFacadeException.noGuid);
      }
      /* See if we have a recurrence id */
      BwDateTime ridObj = null;
      String rid = null;
      TimeZone ridTz = null;
      final RecurrenceId ridp = pl.getProperty(Property.RECURRENCE_ID);
      if (ridp != null) {
        testXparams(ridp, hasXparams);
        ridObj = BwDateTime.makeBwDateTime(ridp);
        if (ridObj.getRange() != null) {
          /* XXX What do I do with it? */
          logger.warn("TRANS-TO_EVENT: Got a recurrence id range");
        }
        rid = ridObj.getDate();
      }
      EventInfo masterEI = null;
      EventInfo evinfo = null;
      final BwEvent ev;
      /* If we have a recurrence id see if we already have the master (we should
       * get a master + all its overrides).
       *
       * If so find the override and use the annnotation or if no override,
       * make one.
       *
       * If no override retrieve the event, add it to our table and then locate the
       * annotation.
       *
       * If there is no annotation, create one.
       *
       * It's possible we have been sent 'detached' instances of a recurring
       * event. This may happen if we are invited to one or more instances of a
       * meeting. In this case we try to retrieve the master and if it doesn't
       * exist we manufacture one. We consider such an instance an update to
       * that instance only and leave the others alone.
       */
      /* We need this in a couple of places */
      final DtStart dtStart = pl.getProperty(Property.DTSTART);
      /*
      if (rid != null) {
        // See if we have a new master event. If so create a proxy to that event.
        masterEI = findMaster(guid, ical.getComponents());
        if (masterEI == null) {
          masterEI = makeNewEvent(cb, chg, entityType, guid, cal);
          BwEvent e = masterEI.getEvent();
          // XXX This seems bogus
          DtStart mdtStart;
          String bogusDate = "19980118T230000";
          if (dtStart.isUtc()) {
            mdtStart = new DtStart(bogusDate + "Z");
          } else if (dtStart.getTimeZone() == null) {
            mdtStart = new DtStart(bogusDate);
          } else {
            mdtStart = new DtStart(bogusDate + "Z", dtStart.getTimeZone());
          }
          setDates(e, mdtStart, null, null, chg);
          e.setRecurring(true);
          e.addRdate(ridObj);
          e.setSuppressed(true);
          ical.addComponent(masterEI);
        }
        if (masterEI != null) {
          evinfo = masterEI.findOverride(rid);
        }
      }
      */
      /* If this is a recurrence instance see if we can find the master
         We only need this because the master may follow the overrides.
       */
      if (rid != null) {
        // See if we have a new master event. If so create a proxy to this event.
        masterEI = findMaster(guid, ical.getComponents());
        if (masterEI != null) {
          evinfo = masterEI.findOverride(rid);
          evinfo.recurrenceSeen = true;
        }
      }
      if ((evinfo == null) &&
          (cal != null) &&
          (cal.getCalType() != BwCalendar.calTypeInbox) &&
          (cal.getCalType() != BwCalendar.calTypePendingInbox) &&
          (cal.getCalType() != BwCalendar.calTypeOutbox)) {
        if (logger.debug()) {
          logger.debug("TRANS-TO_EVENT: try to fetch event with guid=" + guid);
        }
        final GetEntitiesResponse<EventInfo> eisResp =
                cb.getEvent(colPath, guid);
        if (eisResp.isError()) {
          return Response.fromResponse(resp, eisResp);
        }
        final var eis = eisResp.getEntities();
        if (!Util.isEmpty(eis)) {
          if (eis.size() > 1) {
            // DORECUR - wrong again
            return Response.notOk(resp, failed,
                                  "More than one event returned for guid.");
          }
          evinfo = eis.iterator().next();
        }
        if (logger.debug()) {
          if (evinfo != null) {
            logger.debug("TRANS-TO_EVENT: fetched event with guid");
          } else {
            logger.debug("TRANS-TO_EVENT: did not find event with guid");
          }
        }
        if (evinfo != null) {
          if (rid != null) {
            // We just retrieved it's master
            masterEI = evinfo;
            masterEI.setInstanceOnly(true);
            evinfo = masterEI.findOverride(rid);
            evinfo.recurrenceSeen = true;
            ical.addComponent(masterEI);
          } else if (methodType == ScheduleMethods.methodTypeCancel) {
            // This should never have an rid for cancel of entire event.
            evinfo.setInstanceOnly(evinfo.getEvent().getSuppressed());
          } else {
            // Presumably sent an update for the entire event. No longer suppressed master
            evinfo.getEvent().setSuppressed(false);
          }
        } else if (rid != null) {
          /* Manufacture a master for the instance */
          masterEI = CnvUtil.makeNewEvent(cb, entityType, guid, colPath);
          final BwEvent e = masterEI.getEvent();
          // XXX This seems bogus
          final DtStart mdtStart;
          final String bogusDate = "19980118";
          final String bogusTime = "T230000";
          // Base dtstart on the recurrence id.
          final boolean isDateType = ridObj.getDateType();
          if (isDateType) {
            mdtStart = new DtStart(new Date(bogusDate));
          } else if (dtStart.isUtc()) {
            mdtStart = new DtStart(bogusDate + bogusTime + "Z");
          } else if (ridObj.getTzid() == null) {
            mdtStart = new DtStart(bogusDate + bogusTime);
          } else {
            mdtStart = new DtStart(bogusDate + bogusTime,
                                   Timezones.getTz(ridObj.getTzid()));
          }
          IcalUtil.setDates(cb.getPrincipal().getPrincipalRef(),
                            masterEI, mdtStart, null, null);
          e.setRecurring(true);
//          e.addRdate(ridObj);
          final var sum = (Summary)pl.getProperty(Property.SUMMARY);
          e.setSummary(sum.getValue());
          e.setSuppressed(true);
          ical.addComponent(masterEI);
          evinfo = masterEI.findOverride(rid);
          evinfo.recurrenceSeen = true;
          masterEI.setInstanceOnly(rid != null);
        }
      }
      if (evinfo == null) {
        evinfo = CnvUtil.makeNewEvent(cb, entityType, guid, colPath);
      } else if (evinfo.getEvent().getEntityType() != entityType) {
        return Response.notOk(resp, failed,
                              "org.bedework.mismatched.entity.type: " +
                                      val);
      }
      final ChangeTable chg = evinfo.getChangeset(
              cb.getPrincipal().getPrincipalRef());
      if (rid != null) {
        final String evrid = evinfo.getEvent().getRecurrenceId();
        if ((evrid == null) || (!evrid.equals(rid))) {
          logger. warn("Mismatched rid ev=" + evrid + " expected " + rid);
          chg.changed(PropertyInfoIndex.RECURRENCE_ID, evrid, rid); // XXX spurious???
        }
        if (masterEI.getEvent().getSuppressed()) {
          masterEI.getEvent().addRdate(ridObj);
        }
      }
      ev = evinfo.getEvent();
      ev.setScheduleMethod(methodType);
      DtEnd dtEnd = null;
      if (entityType == IcalDefs.entityTypeTodo) {
        final Due due = pl.getProperty(Property.DUE);
        if (due != null ) {
          dtEnd = new DtEnd(due.getParameters(), due.getValue());
        }
      } else {
        dtEnd = pl.getProperty(Property.DTEND);
      }
      final Duration duration = pl.getProperty(Property.DURATION);
      IcalUtil.setDates(cb.getPrincipal().getPrincipalRef(),
                        evinfo, dtStart, dtEnd, duration);
      for (final Property prop: pl) {
        testXparams(prop, hasXparams);
        //debug("ical prop " + prop.getClass().getName());
        String pval = prop.getValue();
        if ((pval != null) && (pval.length() == 0)) {
          pval = null;
        }
        final PropertyInfoIndex pi;
        if (prop instanceof XProperty) {
          pi = PropertyInfoIndex.XPROP;
        } else {
          pi = PropertyInfoIndex.fromName(prop.getName());
        }
        if (pi == null) {
          logger.debug("Unknown property with name " + prop.getName() +
                                " class " + prop.getClass() +
                                " and value " + pval);
          continue;
        }
        chg.present(pi);
        switch (pi) {
          case ACCEPT_RESPONSE:
            /* ------------------- Accept Response -------------------- */
            String sval = prop.getValue();
            if (chg.changed(pi, ev.getPollAcceptResponse(), sval)) {
              ev.setPollAcceptResponse(sval);
            }
            break;
          case ATTACH:
            /* ------------------- Attachment -------------------- */
            chg.addValue(pi, IcalUtil.getAttachment((Attach)prop));
            break;
          case ATTENDEE:
            /* ------------------- Attendee -------------------- */
            if (methodType == ScheduleMethods.methodTypePublish) {
              if (cb.getStrictness() == IcalCallback.conformanceStrict) {
                return Response.notOk(resp, failed,
                                      CalFacadeException.attendeesInPublish);
              }
              //if (cb.getStrictness() == IcalCallback.conformanceWarn) {
              //  warn("Had attendees for PUBLISH");
              //}
            }
            final Attendee attPr = (Attendee)prop;
            if (evinfo.getNewEvent() || !mergeAttendees) {
              chg.addValue(pi, IcalUtil.getAttendee(cb, attPr));
            } else {
              final String pUri = cb.getCaladdr(attPr.getValue());
              if (pUri.equals(attUri)) {
                /* Only update for our own attendee
                 * We're doing a PUT and this must be the attendee updating their
                 * partstat. We don't allow them to change other attendees
                 * whatever the PUT content says.
                 */
                chg.addValue(pi, IcalUtil.getAttendee(cb, attPr));
              } else {
                // Use the value we currently have
                boolean found = false;
                for (final BwAttendee att: ev.getAttendees()) {
                  if (pUri.equals(att.getAttendeeUri())) {
                    chg.addValue(pi, att.clone());
                    found = true;
                    break;
                  }
                }
                if (!found) {
                  // An added attendee
                  final BwAttendee att = IcalUtil
                          .getAttendee(cb, attPr);
                  att.setPartstat(IcalDefs.partstatValNeedsAction);
                  chg.addValue(pi, att);
                }
              }
            }
            break;
          case BUSYTYPE:
            final int ibt = BwEvent.fromBusyTypeString(pval);
            if (chg.changed(pi,
                            ev.getBusyType(),
                            ibt)) {
              ev.setBusyType(ibt);
            }
            break;
          case CATEGORIES:
            /* ------------------- Categories -------------------- */
            final Categories cats = (Categories)prop;
            final TextList cl = cats.getCategories();
            String lang = IcalUtil.getLang(cats);
            if (cl != null) {
              /* Got some categories */
              for (final String wd: cl) {
                if (wd == null) {
                  continue;
                }
                final BwString key = new BwString(lang, wd);
                final var fcResp = cb.findCategory(key);
                final BwCategory cat;
                if (fcResp.isError()) {
                  return Response.fromResponse(resp, fcResp);
                }
                if (fcResp.isNotFound()) {
                  cat = BwCategory.makeCategory();
                  cat.setWord(key);
                  cb.addCategory(cat);
                } else {
                  cat = fcResp.getEntity();
                }
                chg.addValue(pi, cat);
              }
            }
            break;
          case CLASS:
            /* ------------------- Class -------------------- */
            if (chg.changed(pi, ev.getClassification(), pval)) {
              ev.setClassification(pval);
            }
            break;
          case COMMENT:
            /* ------------------- Comment -------------------- */
            chg.addValue(pi,
                         new BwString(null, pval));
            break;
          case COMPLETED:
            /* ------------------- Completed -------------------- */
            if (chg.changed(pi, ev.getCompleted(), pval)) {
              ev.setCompleted(pval);
            }
            break;
          case CONCEPT:
            /* ------------------- Concept -------------------- */
            final Concept c = (Concept)prop;
            final String cval = c.getValue();
            if (cval != null) {
              /* Got a concept */
              chg.addValue(PropertyInfoIndex.XPROP,
                           BwXproperty.makeIcalProperty("CONCEPT",
                                                        null,
                                                        cval));
            }
            break;
          case CONTACT:
            /* ------------------- Contact -------------------- */
            final String altrep = getAltRepPar(prop);
            lang = IcalUtil.getLang(prop);
            final String uid = getUidPar(prop);
            final BwString nm = new BwString(lang, pval);
            BwContact contact = null;
            if (uid != null) {
              final var fcResp = cb.getContact(uid);
              if (fcResp.isError()) {
                return Response.fromResponse(resp, fcResp);
              }
              if (fcResp.isOk()) {
                contact = fcResp.getEntity();
              }
            }
            if (contact == null) {
              final var fcResp = cb.findContact(nm);
              if (fcResp.isError()) {
                return Response.fromResponse(resp, fcResp);
              }
              if (fcResp.isOk()) {
                contact = fcResp.getEntity();
              }
            }
            if (contact == null) {
              contact = BwContact.makeContact();
              contact.setCn(nm);
              contact.setLink(altrep);
              cb.addContact(contact);
            } else {
              contact.setCn(nm);
              contact.setLink(altrep);
            }
            chg.addValue(pi, contact);
            break;
          case CREATED:
            /* ------------------- Created -------------------- */
            if (chg.changed(pi, ev.getCreated(), pval)) {
              ev.setCreated(pval);
            }
            break;
          case DESCRIPTION:
            /* ------------------- Description -------------------- */
            if (chg.changed(pi, ev.getDescription(), pval)) {
              ev.setDescription(pval);
            }
            break;
          case DTEND:
            /* ------------------- DtEnd -------------------- */
            break;
          case DTSTAMP:
            /* ------------------- DtStamp -------------------- */
            ev.setDtstamp(pval);
            break;
          case DTSTART:
            /* ------------------- DtStart -------------------- */
            break;
          case DUE:
            /* -------------------- Due ------------------------ */
            break;
          case DURATION:
            /* ------------------- Duration -------------------- */
            break;
          case EXDATE:
            /* ------------------- ExDate -------------------- */
            chg.addValues(pi,
                          IcalUtil.makeDateTimes((DateListProperty)prop));
            break;
          case EXRULE:
            /* ------------------- ExRule -------------------- */
            chg.addValue(pi, pval);
            break;
          case FREEBUSY:
            /* ------------------- freebusy -------------------- */
            final FreeBusy fbusy = (FreeBusy)prop;
            final PeriodList perpl = fbusy.getPeriods();
            final Parameter par = IcalUtil.getParameter(fbusy, "FBTYPE");
            final int fbtype;
            if (par == null) {
              fbtype = BwFreeBusyComponent.typeBusy;
            } else if (par.equals(FbType.BUSY)) {
              fbtype = BwFreeBusyComponent.typeBusy;
            } else if (par.equals(FbType.BUSY_TENTATIVE)) {
              fbtype = BwFreeBusyComponent.typeBusyTentative;
            } else if (par.equals(FbType.BUSY_UNAVAILABLE)) {
              fbtype = BwFreeBusyComponent.typeBusyUnavailable;
            } else if (par.equals(FbType.FREE)) {
              fbtype = BwFreeBusyComponent.typeFree;
            } else {
              if (logger.debug()) {
                logger.debug("Unsupported parameter " + par.getName());
              }
              return Response.notOk(resp, failed,
                                    "Unsupported parameter " +
                                            par.getName());
            }
            final BwFreeBusyComponent fbc = new BwFreeBusyComponent();
            fbc.setType(fbtype);
            for (final Period per : perpl) {
              fbc.addPeriod(per);
            }
            ev.addFreeBusyPeriod(fbc);
            break;
          case GEO:
            /* ------------------- Geo -------------------- */
            final Geo g = (Geo)prop;
            final BwGeo geo = new BwGeo(g.getLatitude(),
                                        g.getLongitude());
            if (chg.changed(pi, ev.getGeo(), geo)) {
              ev.setGeo(geo);
            }
            break;
          case LAST_MODIFIED:
            /* ------------------- LastModified -------------------- */
            if (chg.changed(pi, ev.getLastmod(), pval)) {
              ev.setLastmod(pval);
            }
            break;
          case LOCATION:
            /* ------------------- Location -------------------- */
            BwLocation loc = null;
            //String uid = getUidPar(prop);
            /* At the moment Mozilla lightning is broken and this leads to all
           * sorts of problems.
            if (uid != null) {
              loc = cb.getLocation(uid);
            }
           */
            lang = IcalUtil.getLang(prop);
            BwString addr = null;
            if (pval != null) {
              if (loc == null) {
                addr = new BwString(lang, pval);
                final var fcResp = cb.findLocation(addr);
                if (fcResp.isError()) {
                  return Response.fromResponse(resp, fcResp);
                }
                if (fcResp.isOk()) {
                  loc = fcResp.getEntity();
                }
              }
              if (loc == null) {
                loc = BwLocation.makeLocation();
                loc.setAddress(addr);
                cb.addLocation(loc);
              }
            }
            final BwLocation evloc = ev.getLocation();
            if (chg.changed(pi, evloc, loc)) {
              // CHGTBL - this only shows that it's a different location object
              ev.setLocation(loc);
            } else if ((loc != null) && (evloc != null)) {
              // See if the value is changed
              final String evval = evloc.getAddress().getValue();
              final String inval = loc.getAddress().getValue();
              if (!evval.equals(inval)) {
                chg.changed(pi, evval, inval);
                evloc.getAddress().setValue(inval);
              }
            }
            break;
          case ORGANIZER:
            /* ------------------- Organizer -------------------- */
            final BwOrganizer org = IcalUtil.getOrganizer(cb, (Organizer)prop);
            final BwOrganizer evorg = ev.getOrganizer();
            final BwOrganizer evorgCopy;
            if (evorg == null) {
              evorgCopy = null;
            } else {
              evorgCopy = (BwOrganizer)evorg.clone();
            }
            if (chg.changed(pi, evorgCopy, org)) {
              if (evorg == null) {
                ev.setOrganizer(org);
              } else {
                evorg.update(org);
              }
            }
            break;
          case PERCENT_COMPLETE:
            /* ------------------- PercentComplete -------------------- */
            Integer ival = ((PercentComplete)prop).getPercentage();
            if (chg.changed(pi, ev.getPercentComplete(), ival)) {
              ev.setPercentComplete(ival);
            }
            break;
          case POLL_MODE:
            /* ------------------- Poll mode -------------------- */
            sval = prop.getValue();
            if (chg.changed(pi, ev.getPollMode(), sval)) {
              ev.setPollMode(sval);
            }
            break;
          case POLL_PROPERTIES:
            /* ------------------- Poll properties ---------------- */
            sval = prop.getValue();
            if (chg.changed(pi, ev.getPollProperties(), sval)) {
              ev.setPollProperties(sval);
            }
            break;
          case POLL_WINNER:
            /* ------------------- Poll winner -------------------- */
            ival = ((PollWinner)prop).getPollwinner();
            if (chg.changed(pi, ev.getPollWinner(), ival)) {
              ev.setPollWinner(ival);
            }
            break;
          case PRIORITY:
            /* ------------------- Priority -------------------- */
            ival = ((Priority)prop).getLevel();
            if (chg.changed(pi, ev.getPriority(), ival)) {
              ev.setPriority(ival);
            }
            break;
          case RDATE:
            /* ------------------- RDate -------------------- */
            chg.addValues(pi,
                          IcalUtil.makeDateTimes((DateListProperty)prop));
            break;
          case RECURRENCE_ID:
            /* ------------------- RecurrenceID -------------------- */
            // Done above
            break;
          case RELATED_TO:
            /* ------------------- RelatedTo -------------------- */
            final RelatedTo irelto = (RelatedTo)prop;
            final BwRelatedTo relto = new BwRelatedTo();
            final String parval = IcalUtil.getParameterVal(irelto,
                                                           "RELTYPE");
            if (parval != null) {
              relto.setRelType(parval);
            }
            relto.setValue(irelto.getValue());
            if (chg.changed(pi, ev.getRelatedTo(), relto)) {
              ev.setRelatedTo(relto);
            }
            break;
          case REQUEST_STATUS:
            /* ------------------- RequestStatus -------------------- */
            final BwRequestStatus rs = BwRequestStatus
                    .fromRequestStatus((RequestStatus)prop);
            chg.addValue(pi, rs);
            break;
          case RESOURCES:
            /* ------------------- Resources -------------------- */
            final TextList rl = ((Resources)prop).getResources();
            if (rl != null) {
              /* Got some resources */
              lang = IcalUtil.getLang(prop);
              for (final String s: rl) {
                final BwString rsrc = new BwString(lang,
                                                   s);
                chg.addValue(pi, rsrc);
              }
            }
            break;
          case RRULE:
            /* ------------------- RRule -------------------- */
            chg.addValue(pi, pval);
            break;
          case SEQUENCE:
            /* ------------------- Sequence -------------------- */
            final int seq = ((Sequence)prop).getSequenceNo();
            if (seq != ev.getSequence()) {
              chg.changed(pi, ev.getSequence(), seq);
              ev.setSequence(seq);
            }
            break;
          case STATUS:
            /* ------------------- Status -------------------- */
            if (chg.changed(pi, ev.getStatus(), pval)) {
              ev.setStatus(pval);
            }
            break;
          case SUMMARY:
            /* ------------------- Summary -------------------- */
            if (chg.changed(pi, ev.getSummary(), pval)) {
              ev.setSummary(pval);
            }
            break;
          case TRANSP:
            /* ------------------- Transp -------------------- */
            if (chg.changed(pi,
                            ev.getPeruserTransparency(
                                    cb.getPrincipal()
                                            .getPrincipalRef()),
                            pval)) {
              final BwXproperty pu = ev.setPeruserTransparency(
                      cb.getPrincipal().getPrincipalRef(),
                      pval);
              if (pu != null) {
                chg.addValue(PropertyInfoIndex.XPROP, pu);
              }
            }
            break;
          case UID:
            /* ------------------- Uid -------------------- */
            /* We did this above */
            break;
          case URL:
            /* ------------------- Url -------------------- */
            if (chg.changed(pi, ev.getLink(), pval)) {
              ev.setLink(pval);
            }
            break;
          case XPROP:
            /* ------------------------- x-property --------------------------- */
            final String name = prop.getName();
            if (name.equalsIgnoreCase(BwXproperty.bedeworkCost)) {
              if (chg.changed(PropertyInfoIndex.COST, ev.getCost(),
                              pval)) {
                ev.setCost(pval);
              }
              break;
            }
            if (name.equalsIgnoreCase(BwXproperty.xBedeworkCategories)) {
              if (checkCategory(cb, chg, ev, null, pval)) {
                break;
              }
            }
            if (name.equalsIgnoreCase(BwXproperty.xBedeworkLocation)) {
              if (checkLocation(cb, chg, ev, prop)) {
                break;
              }
            }
            if (name.equalsIgnoreCase(BwXproperty.xBedeworkContact)) {
              if (checkContact(cb, chg, ev, null, pval)) {
                break;
              }
            }
            /* See if this is an x-category that can be
               converted to a real category
              */
            final XProperty xp = (XProperty)prop;
            chg.addValue(PropertyInfoIndex.XPROP,
                         new BwXproperty(name,
                                         xp.getParameters()
                                                 .toString(),
                                         pval));
            break;
          default:
            if (logger.debug()) {
              logger.debug("Unsupported property with index " + pi +
                                    "; class " + prop.getClass() +
                                    " and value " + pval);
            }
        }
      }
      /* =================== Process sub-components =============== */
      final ComponentList<Component> subComps;
      if (val instanceof ComponentContainer) {
        subComps = ((ComponentContainer<Component>)val).getComponents();
      } else {
        subComps = null;
      }
      final Set<Integer> pids;
      if (vpoll) {
        pids = new TreeSet<>();
        final BwEvent vp = evinfo.getEvent();
        if (!Util.isEmpty(vp.getPollItems())) {
          vp.clearPollItems();
        }
      } else {
        pids = null;
      }
      if (!Util.isEmpty(subComps)) {
        for (final var subComp: subComps) {
          if (subComp instanceof Available) {
            if (!(val instanceof VAvailability)) {
              return Response.error(resp, "AVAILABLE only valid in VAVAILABLE");
            }
            final var avlResp = processAvailable(cb, cal,
                                                 ical,
                                                 (VAvailability)val,
                                                 (Available)subComp,
                                                 evinfo);
            if (!avlResp.isOk()) {
              return Response.fromResponse(resp, avlResp);
            }
            continue;
          }
          if (subComp instanceof Participant) {
            if (vpoll) {
              final var vresp = processVoter(cb,
                                             (VPoll)val,
                                             (Participant)subComp,
                                             evinfo,
                                             chg,
                                             mergeAttendees);
              if (!vresp.isOk()) {
                return Response.fromResponse(resp, vresp);
              }
              continue;
            }
            logger.warn("Unimplemented Participant object");
            continue;
          }
          if (subComp instanceof VResource) {
            logger.warn("Unimplemented VResource object");
            continue;
          }
          if (subComp instanceof VLocation) {
            logger.warn("Unimplemented VLocation object");
            continue;
          }
          if (subComp instanceof VAlarm) {
            final var aresp = VAlarmUtil.processAlarm(cb,
                                                      val,
                                                      (VAlarm)subComp,
                                                      ev,
                                                      currentPrincipal,
                                                      chg);
            if (!aresp.isOk()) {
              return Response.fromResponse(resp, aresp);
            }
            continue;
          }
          if (vpoll && (event || task)) {
            final var vresp = processCandidate((VPoll)val,
                                               subComp,
                                               evinfo,
                                               pids,
                                               chg);
            if (!vresp.isOk()) {
              return Response.fromResponse(resp, vresp);
            }
            continue;
          }
          logger.warn("Unimplemented Component object: " + subComp);
        }
      }
      /* Fix up timestamps. */
      if (ev.getCreated() == null) {
        if (ev.getLastmod() != null) {
          ev.setCreated(ev.getLastmod());
          chg.changed(PropertyInfoIndex.CREATED, null, ev.getCreated());
        } else {
          ev.updateDtstamp();
          chg.changed(PropertyInfoIndex.CREATED, null, ev.getCreated());
          chg.changed(PropertyInfoIndex.LAST_MODIFIED, null, ev.getLastmod());
        }
      }
      if (ev.getLastmod() == null) {
        // created cannot be null now
        ev.setLastmod(ev.getCreated());
        chg.changed(PropertyInfoIndex.LAST_MODIFIED, null, ev.getLastmod());
      }
      processTimezones(ev, ical, chg);
      /* Remove any recipients and originator
       */
      if (ev.getRecipients() != null) {
        ev.getRecipients().clear();
      }
      ev.setOriginator(null);
      if (hasXparams.value) {
        /* Save a text copy of the entire event as an x-property */
        final Component valCopy = val.copy();
        /* Remove potentially large values */
        final Description desp = valCopy.getProperty(Property.DESCRIPTION);
        if (desp != null) {
          desp.setValue(null);
        }
        final Attach attachp = valCopy.getProperty(Property.ATTACH);
        // Don't store the entire attachment - we just need the parameters.
        if (attachp != null) {
          final Value v = attachp.getParameter(Parameter.VALUE);
          if (v != null) {
            attachp.setValue(String.valueOf(attachp.getValue().hashCode()));
          }
        }
        chg.addValue(PropertyInfoIndex.XPROP,
                     new BwXproperty(BwXproperty.bedeworkIcal,
                                     null,
                                     valCopy.toString()));
      }
      chg.processChanges(ev, true, false);
      ev.setRecurring(ev.isRecurringEntity());
      if (logger.debug()) {
        logger.debug(chg.toString());
        logger.debug(ev.toString());
      }
      if (masterEI != null) {
        // Just return notfound as this event is on its override list
        return Response.notFound(resp);
      }
      resp.setEntity(evinfo);
      return resp;
    } catch (final Throwable t) {
      if (logger.debug()) {
        logger.error(t);
      }
      return Response.error(resp, t);
    }
  }
String attUri = null;
if (mergeAttendees) {
// We'll need this later.
attUri = cb.getCaladdr(cb.getPrincipal().getPrincipalRef());
}
}
String currentPrincipal = null;
final BwPrincipal principal = cb.getPrincipal();
if (principal != null) {
currentPrincipal = principal.getPrincipalRef();
}
final Holder<Boolean> hasXparams = new Holder<>(Boolean.FALSE);
final int methodType = ical.getMethodType();
String attUri = null;
if (mergeAttendees) {
// We'll need this later.
attUri = cb.getCaladdr(cb.getPrincipal().getPrincipalRef());
}
final String colPath;
if (cal == null) {
colPath = null;
} else {
colPath = cal.getPath();
}
try {
final PropertyList<Property> pl = val.getProperties();
public static GetEntityResponse<EventInfo> toEvent(
final IcalCallback cb,
final BwCalendar cal,
final Icalendar ical,
final Component val,
final boolean mergeAttendees) {
final var resp = new GetEntityResponse<EventInfo>();
if (val == null) {
return Response.notOk(resp, failed, "No component supplied");
}
String currentPrincipal = null;
final BwPrincipal principal = cb.getPrincipal();
if (principal != null) {
currentPrincipal = principal.getPrincipalRef();
}
final Holder<Boolean> hasXparams = new Holder<>(Boolean.FALSE);
final int methodType = ical.getMethodType();
String attUri = null;
if (mergeAttendees) {
// We'll need this later.
attUri = cb.getCaladdr(cb.getPrincipal().getPrincipalRef());
}
final String colPath;
if (cal == null) {
colPath = null;
} else {
colPath = cal.getPath();
}
try {
final PropertyList<Property> pl = val.getProperties();
boolean vpoll = false;
boolean event = false;
boolean task = false;
if (pl == null) {
// Empty component
return Response.notOk(resp, failed, "Empty component");
}
final int entityType;
if (val instanceof VEvent) {
entityType = IcalDefs.entityTypeEvent;