/**
   * Ctor.
   *
   * @param revisionEventTypeName name
   * @param spec specification
   * @param statementStopService for stop handling
   * @param eventAdapterService for nested property handling
   */
  public VAERevisionProcessorDeclared(
      String revisionEventTypeName,
      RevisionSpec spec,
      StatementStopService statementStopService,
      EventAdapterService eventAdapterService,
      EventTypeIdGenerator eventTypeIdGenerator) {
    super(spec, revisionEventTypeName, eventAdapterService);

    // on statement stop, remove versions
    statementStopService.addSubscriber(
        new StatementStopCallback() {
          public void statementStopped() {
            statePerKey.clear();
          }
        });

    this.statePerKey = new HashMap<MultiKeyUntyped, RevisionStateDeclared>();
    this.baseEventType = spec.getBaseEventType();
    this.fullKeyGetters = PropertyUtility.getGetters(baseEventType, spec.getKeyPropertyNames());

    // sort non-key properties, removing keys
    groups =
        PropertyUtility.analyzeGroups(
            spec.getChangesetPropertyNames(), spec.getDeltaTypes(), spec.getDeltaNames());
    Map<String, RevisionPropertyTypeDesc> propertyDesc = createPropertyDescriptors(spec, groups);

    typeDescriptors =
        PropertyUtility.getPerType(
            groups, spec.getChangesetPropertyNames(), spec.getKeyPropertyNames());
    EventTypeMetadata metadata =
        EventTypeMetadata.createValueAdd(
            revisionEventTypeName, EventTypeMetadata.TypeClass.REVISION);
    revisionEventType =
        new RevisionEventType(
            metadata,
            eventTypeIdGenerator.getTypeId(revisionEventTypeName),
            propertyDesc,
            eventAdapterService);
  }
  /**
   * Creates property descriptors for revision.
   *
   * @param spec specifies revision
   * @param groups the groups that group properties
   * @return map of property and descriptor
   */
  public static Map<String, RevisionPropertyTypeDesc> createPropertyDescriptors(
      RevisionSpec spec, PropertyGroupDesc groups[]) {
    Map<String, int[]> propsPerGroup = PropertyUtility.getGroupsPerProperty(groups);

    Map<String, RevisionPropertyTypeDesc> propertyDesc =
        new HashMap<String, RevisionPropertyTypeDesc>();
    int count = 0;

    for (String property : spec.getChangesetPropertyNames()) {
      EventPropertyGetter fullGetter = spec.getBaseEventType().getGetter(property);
      int propertyNumber = count;
      int[] propGroupsProperty = propsPerGroup.get(property);
      final RevisionGetterParameters params =
          new RevisionGetterParameters(property, propertyNumber, fullGetter, propGroupsProperty);

      // if there are no groups (full event property only), then simply use the full event getter
      EventPropertyGetter revisionGetter =
          new EventPropertyGetter() {
            public Object get(EventBean eventBean) throws PropertyAccessException {
              RevisionEventBeanDeclared riv = (RevisionEventBeanDeclared) eventBean;
              return riv.getVersionedValue(params);
            }

            public boolean isExistsProperty(EventBean eventBean) {
              return true;
            }

            public Object getFragment(EventBean eventBean) {
              return null; // fragments no provided by revision events
            }
          };

      Class type = spec.getBaseEventType().getPropertyType(property);
      RevisionPropertyTypeDesc propertyTypeDesc =
          new RevisionPropertyTypeDesc(revisionGetter, params, type);
      propertyDesc.put(property, propertyTypeDesc);
      count++;
    }

    for (String property : spec.getBaseEventOnlyPropertyNames()) {
      final EventPropertyGetter fullGetter = spec.getBaseEventType().getGetter(property);

      // if there are no groups (full event property only), then simply use the full event getter
      EventPropertyGetter revisionGetter =
          new EventPropertyGetter() {
            public Object get(EventBean eventBean) throws PropertyAccessException {
              RevisionEventBeanDeclared riv = (RevisionEventBeanDeclared) eventBean;
              return fullGetter.get(riv.getLastBaseEvent());
            }

            public boolean isExistsProperty(EventBean eventBean) {
              return true;
            }

            public Object getFragment(EventBean eventBean) {
              return null; // fragments no provided by revision events
            }
          };

      Class type = spec.getBaseEventType().getPropertyType(property);
      RevisionPropertyTypeDesc propertyTypeDesc =
          new RevisionPropertyTypeDesc(revisionGetter, null, type);
      propertyDesc.put(property, propertyTypeDesc);
      count++;
    }

    count = 0;
    for (String property : spec.getKeyPropertyNames()) {
      final int keyPropertyNumber = count;

      EventPropertyGetter revisionGetter =
          new EventPropertyGetter() {
            public Object get(EventBean eventBean) throws PropertyAccessException {
              RevisionEventBeanDeclared riv = (RevisionEventBeanDeclared) eventBean;
              return riv.getKey().getKeys()[keyPropertyNumber];
            }

            public boolean isExistsProperty(EventBean eventBean) {
              return true;
            }

            public Object getFragment(EventBean eventBean) {
              return null;
            }
          };

      Class type = spec.getBaseEventType().getPropertyType(property);
      RevisionPropertyTypeDesc propertyTypeDesc =
          new RevisionPropertyTypeDesc(revisionGetter, null, type);
      propertyDesc.put(property, propertyTypeDesc);
      count++;
    }

    return propertyDesc;
  }