static {
    delegates.add(new DatatypeStateEntityListener());
    logger.info("Adding DatatypeStateEntityListener to NabuccoEntityListener.");

    delegates.add(new DatatypeLoggingEntityListener());
    logger.info("Adding DatatypeLoggingEntityListener to NabuccoEntityListener.");
  }
  /** {@inheritDoc} */
  @Override
  public void visit(Foreach foreach, TestScriptResult argument) throws TestScriptException {
    getContext().setCurrentTestScriptElement(foreach);
    Name elementName = foreach.getElementName();
    PropertyReference iterableId = foreach.getIterableRef();
    Property iterableProperty = getContext().getProperty(iterableId);

    if (iterableProperty == null) {
      throw new TestScriptException("Property '" + iterableId + "' not found in context");
    }

    PropertyIterator propertyIterator = new PropertyIterator(iterableProperty);

    if (getContext().getProperty(elementName) != null) {
      throw new TestScriptException(
          "Foreach configuration error -> Property '"
              + elementName
              + "' already exists in context");
    }

    logger.info("Starting Foreach-Loop: " + foreach.getId());
    Iterator<Property> iterator = propertyIterator.iterator();
    Property currentProp = null;

    while (iterator.hasNext()) {
      currentProp = iterator.next();

      if (logger.isDebugEnabled()) {
        logger.debug(
            "Next element: id="
                + currentProp.getName().getValue()
                + ", type="
                + currentProp.getType());
      }

      Property currentClone = currentProp.cloneObject();
      currentClone.setName(elementName);
      getContext().put(currentClone);

      try {
        super.visit(foreach.getTestScriptElementList(), argument);
      } catch (BreakLoopException ex) {
        getContext().remove(currentClone);
        break;
      }

      // remove property with id=elementId from context
      getContext().remove(currentClone);
    }
  }
  /**
   * Called before an entity is updated to the database.
   *
   * @param entity the entity to update
   */
  public void preUpdate(Object entity) {
    if (!(entity instanceof Datatype)) {
      logger.warning("Entity to update is not of type Datatype.");
      return;
    }

    for (EntityListener listener : delegates) {
      listener.postPersist((Datatype) entity);
    }
  }
  /**
   * Called after an entity is load from the database.
   *
   * @param entity the loaded entity
   */
  public void postLoad(Object entity) {
    if (!(entity instanceof Datatype)) {
      logger.warning("Loaded entity is not of type Datatype.");
      return;
    }

    for (EntityListener listener : delegates) {
      listener.postLoad((Datatype) entity);
    }
  }
  @Override
  public ActionResponse execute(
      TestContext context,
      PropertyList propertyList,
      List<Metadata> metadataList,
      SubEngineActionType actionType)
      throws SubEngineException {

    Metadata metadata = null;

    try {
      // Validation
      this.validateArguments(context, propertyList, metadataList, actionType);

      SwingActionType swingAction = (SwingActionType) actionType;

      // Init result
      metadata = metadataList.get(metadataList.size() - 1);
      this.initResult(context, metadata, actionType);

      //            this.validateComponent(propertyList, swingAction);

      // Execute Command
      ExecutionCommand command = createCommand(propertyList, metadata, swingAction);
      Integer timeout = getTimeout(metadata, swingAction);
      Property reply = executeCommand(context, command, timeout);

      if (reply != null) {
        PropertyList returnList = PropertyHelper.createPropertyList("Return");
        PropertyHelper.add(reply, returnList);
        result.setReturnProperties(returnList);
      }

    } catch (SwingValidationException e) {
      logger.error(e, "Error validating Swing component.");
      failResult(metadata, actionType, e.getMessage());
    } catch (Exception e) {
      logger.error(e, "Error during Swing component execution.");
      failResult(metadata, actionType);
    }

    return result;
  }
  /**
   * Extracts the timeout time for the current action on the {@link Metadata} component.
   *
   * @param metadata the current {@link Metadata} object
   * @param actionType the type of action
   * @return the wait time for this object and action
   */
  private Integer getTimeout(Metadata metadata, SwingActionType actionType) {

    NumericProperty property = null;

    try {
      switch (actionType) {
        case LEFTCLICK:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_CLICK);
          break;
        case RIGHTCLICK:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_CLICK);
          break;
        case DOUBLECLICK:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_CLICK);
          break;
        case FIND:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_CLICK);
          break;
        case ENTER:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_ENTER);
          break;
        case READ:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_READ);
          break;
        case COUNT:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_READ);
          break;
        case IS_AVAILABLE:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_READ);
          break;
      }

    } catch (Exception e) {
      logger.warning(
          e,
          "Problem extracting wait time for ",
          metadata.getName().getValue(),
          ", using DEFAULT.");
    }

    Integer waitTime =
        property != null ? property.getValue().getValue().intValue() : DEFAULT_WAIT_TIME;

    return waitTime;
  }
  /**
   * Resolve the datatype with the given id from the table model.
   *
   * @param rowId the id of the datatype to resolve from the table model
   * @return the datatype with the given id, or null if the table model does not contain a datatype
   *     with the given id
   */
  protected Datatype getDatatypeById(String rowId) {
    if (rowId == null || rowId.isEmpty()) {
      return null;
    }

    try {
      long id = Long.parseLong(rowId);

      if (tableModel == null) {
        return null;
      }

      return this.tableModel.getDatatypeByObjectId(id);
    } catch (NumberFormatException nfe) {
      logger.warning(nfe, "Cannot parse id '", rowId, "' as long.");
    }

    return null;
  }
  @Override
  public void relateAfter(ServiceMessage requestMessage, ServiceMessage responseMessage)
      throws RelatingException {

    if (responseMessage == null) {
      return;
    }

    try {
      ComponentRelationRelatingVisitor visitor =
          new ComponentRelationRelatingVisitor(super.getContext());
      responseMessage.accept(visitor);
    } catch (Exception e) {
      logger.error(
          e,
          "Error maintaining component relations for '"
              + requestMessage.getClass().getName()
              + "'.");
    }
  }
  @Override
  public NabucoExtensionContainer parseExtension(Element e) throws ExtensionParserException {

    logger.debug("Parsing GeoConfig configuration");

    try {
      GeoConfigExtension ext = new GeoConfigExtension();
      ExtensionId id = new ExtensionId(e.getAttribute(ATTR_ID));
      ext.setIdentifier(id);

      List<Element> schmemaList = getElementsByTagName(e, ELEMENT_GEOSCHEMA);
      List<GeoSchemaExtension> resList = ext.getGeoSchemaList();

      for (Element schemaElement : schmemaList) {

        GeoSchemaExtension gs = new GeoSchemaExtension();
        gs.setLocale(getStringProperty(schemaElement, ATTR_LOCALE));

        resList.add(gs);

        List<Element> geoRegionList = getElementsByTagName(schemaElement, ELEMENT_GEOREGION);
        for (Element geoRegionElement : geoRegionList) {
          GeoRegionExtension gr = new GeoRegionExtension();
          gr.setId(getStringProperty(geoRegionElement, ATTR_ID));
          gr.setType(getStringProperty(geoRegionElement, ATTR_TYPE));
          gr.setOptional(getBooleanProperty(geoRegionElement, ATTR_OPTIONAL));
          gs.getGeoRegionList().add(gr);
        }
      }

      return new NabucoExtensionContainer(ext);

    } catch (ExtensionException ex) {
      throw new ExtensionParserException(ex);
    }
  }