@Override
  public void addMetaData(final IMetaData metaData) throws ForwardedRuntimeException {
    if (metaData == null) {
      throw new ForwardedRuntimeException(MetaDataCollectionException.class, 1002);
    } else if (metaData instanceof IIdentifiedMetaData == false) {
      throw new ForwardedRuntimeException(
          MetaDataCollectionException.class, 1001, metaData.getClass().getSimpleName(), metaData);
    }
    final IIdentifiedMetaData idMetaData = (IIdentifiedMetaData) metaData;
    final String descModelId = idMetaData.getDescriptorModelId();

    lock.writeLock().lock();
    try {
      final LoadedMetaData curMetaData = this.metaData.get(descModelId);

      if (curMetaData == null || curMetaData.size() == 0) {
        if (idMetaData instanceof LoadedMetaData) {
          this.metaData.put(descModelId, (LoadedMetaData) idMetaData);
        } else {
          final LoadedMetaData nmd = new LoadedMetaData(descModelId);
          nmd.addValues(idMetaData.getIdentifiedValues());
          this.metaData.put(descModelId, nmd);
        }
      } else {
        combineMetaData(descModelId, curMetaData, idMetaData);
      }
    } finally {
      lock.writeLock().unlock();
    }
  }
  /**
   * Checks if the specified {@code metaData} contains an information with the specified {@code id}
   * and {@code value}.
   *
   * @param metaData the meta-data to look into
   * @param id the identifier to search for
   * @param value the value to search for
   * @return (@code true) if the {@code MetaData} contains the specified entry, otherwise (@code
   *     false)
   */
  protected boolean contains(final LoadedMetaData metaData, final Object id, final Object value) {
    if (metaData == null) {
      return false;
    }

    final Map<Object, Object> curValues = metaData.getIdentifiedValues();
    if (curValues.containsKey(id)) {
      final Object curValue = curValues.get(id);
      return Objects.equals(curValue, value);
    } else {
      return false;
    }
  }
  /**
   * This method is used to combine the current {@code Collection} of meta-data associated with a
   * {@code DescriptorModel} with the newly added once.
   *
   * @param descModelId the identifier of the {@code DescriptorModel}
   * @param current the current meta-data
   * @param added the meta-data to be added
   * @throws ForwardedRuntimeException if the specified meta-data is invalid
   */
  protected void combineMetaData(
      final String descModelId, final LoadedMetaData current, final IIdentifiedMetaData added)
      throws ForwardedRuntimeException {
    if (added == null) {
      return;
    }

    /*
     * We have identifiers to be added and we already have an identifier
     * set.
     *
     * currentDefMetaData might be null, or not. Therefore we have to
     * consider it.
     */
    final Map<Object, Object> curValues = current.getIdentifiedValues();
    final Map<Object, Object> addedValues = added.getIdentifiedValues();

    // add the values, but make sure there is nothing modified
    for (final Entry<Object, Object> e : addedValues.entrySet()) {
      final Object k = e.getKey();
      final Object v = e.getValue();

      if (curValues.containsKey(k)) {
        final Object curValue = curValues.get(k);
        if (!Objects.equals(curValue, v)) {
          throw new ForwardedRuntimeException(
              MetaDataCollectionException.class, 1000, k, curValue, v);
        }
      } else {
        current.addValue(k, v);
      }
    }

    // create a new id meta-data set
    current.addValues(addedValues);
  }
  /**
   * Adds the specified {@code Descriptor} to the cache.
   *
   * @param desc the {@code Descriptor} to be added
   */
  public void addDescriptor(final Descriptor<?, ?, ?> desc) {
    final LoadedMetaData metaData = new LoadedMetaData(desc.getModelId());
    metaData.addValue(desc.getId(), desc.getValue());

    this.addMetaData(metaData);
  }