public void setAttribute(
      final String namespace, final String name, final Object value, final boolean notifyChange) {
    if (copyOnWrite) {
      this.attributes = attributes.clone();
      this.copyOnWrite = false;
    }

    final Object oldValue = attributes.setAttribute(namespace, name, value);
    if (cachedAttributes != null) {
      if (cachedAttributes.getChangeTracker() != attributes.getChangeTracker()) {
        cachedAttributes = null;
      }
    }
    if (AttributeNames.Core.NAMESPACE.equals(namespace)
        && AttributeNames.Core.ELEMENT_TYPE.equals(name)) {
      if (value instanceof ElementType) {
        this.elementType = (ElementType) value;
      } else {
        this.elementType = LegacyType.INSTANCE;
      }
    }

    if (notifyChange) {
      notifyNodePropertiesChanged(new AttributeChange(namespace, name, oldValue, value));
    }
  }
  /**
   * A helper method that serializes the element object.
   *
   * @param stream the stream to which the element should be serialized.
   * @throws IOException if an IO error occured or a property was not serializable.
   */
  private void writeObject(final ObjectOutputStream stream) throws IOException {
    stream.defaultWriteObject();
    final ReportAttributeMap attributes = this.attributes;
    stream.writeLong(attributes.getChangeTracker());
    final String[] nameSpaces = attributes.getNameSpaces();
    stream.writeObject(nameSpaces);
    for (int i = 0; i < nameSpaces.length; i++) {
      final String nameSpace = nameSpaces[i];
      final String[] names = attributes.getNames(nameSpace);
      stream.writeObject(names);
      for (int j = 0; j < names.length; j++) {
        final String name = names[j];
        final Object attribute = attributes.getAttribute(nameSpace, name);

        final AttributeMetaData data = getMetaData().getAttributeDescription(nameSpace, name);
        if (data != null) {
          if (data.isTransient()) {
            stream.writeByte(1);
            continue;
          }

          if (attribute instanceof ResourceKey) {
            final ResourceKey key = (ResourceKey) attribute;
            final ResourceKey parent = key.getParent();
            if (AttributeNames.Core.NAMESPACE.equals(nameSpace)
                && (AttributeNames.Core.CONTENT_BASE.equals(name)
                    || AttributeNames.Core.SOURCE.equals(name))) {
              if (parent != null) {
                // unwrap the content base attribute. After deserialization, the report assumes the
                // bundle-location
                // as content base, as the bundle will be gone.
                if (isKeySerializable(parent)) {
                  stream.writeByte(0);
                  SerializerHelper.getInstance().writeObject(parent, stream);
                } else {
                  stream.writeByte(1);
                }
              } else {
                // great, the report was never part of a bundle. That makes life easier and the key
                // should be
                // safely serializable too.

                if (isKeySerializable(key)) {
                  stream.writeByte(0);
                  SerializerHelper.getInstance().writeObject(key, stream);
                } else {
                  stream.writeByte(1);
                }
              }
            } else {
              if ("Resource".equals(data.getValueRole()) || parent != null) {
                stream.writeByte(0);
                try {
                  final ResourceKey resourceKey =
                      ResourceKeyUtils.embedResourceInKey(
                          locateResourceManager(), key, key.getFactoryParameters());
                  SerializerHelper.getInstance().writeObject(resourceKey, stream);
                } catch (ResourceException e) {
                  throw new IOException("Failed to convert resource-key into byte-array key: " + e);
                }
              } else {
                stream.writeByte(0);
                SerializerHelper.getInstance().writeObject(attribute, stream);
              }
            }
          } else if (SerializerHelper.getInstance().isSerializable(attribute)) {
            stream.writeByte(0);
            SerializerHelper.getInstance().writeObject(attribute, stream);
          } else {
            stream.writeByte(1);
          }
        } else if (attribute instanceof String) {
          stream.writeByte(0);
          SerializerHelper.getInstance().writeObject(attribute, stream);
        } else {
          stream.writeByte(1);
        }
      }
    }
  }
  public void copyInto(final Element target) {
    final ElementMetaData metaData = getMetaData();
    final String[] attributeNamespaces = getAttributeNamespaces();
    for (int i = 0; i < attributeNamespaces.length; i++) {
      final String namespace = attributeNamespaces[i];
      final String[] attributeNames = getAttributeNames(namespace);
      for (int j = 0; j < attributeNames.length; j++) {
        final String name = attributeNames[j];
        final AttributeMetaData attributeDescription =
            metaData.getAttributeDescription(namespace, name);
        if (attributeDescription == null) {
          continue;
        }
        if (attributeDescription.isTransient()) {
          continue;
        }
        if (attributeDescription.isComputed()) {
          continue;
        }
        if (AttributeNames.Core.ELEMENT_TYPE.equals(name)
            && AttributeNames.Core.NAMESPACE.equals(namespace)) {
          continue;
        }
        target.setAttribute(namespace, name, getAttribute(namespace, name), false);
      }
    }

    final String[] attrExprNamespaces = getAttributeExpressionNamespaces();
    for (int i = 0; i < attrExprNamespaces.length; i++) {
      final String namespace = attrExprNamespaces[i];
      final String[] attributeNames = getAttributeExpressionNames(namespace);
      for (int j = 0; j < attributeNames.length; j++) {
        final String name = attributeNames[j];

        final AttributeMetaData attributeDescription =
            metaData.getAttributeDescription(namespace, name);
        if (attributeDescription == null) {
          continue;
        }
        if (attributeDescription.isTransient()) {
          continue;
        }
        target.setAttributeExpression(namespace, name, getAttributeExpression(namespace, name));
      }
    }

    final ElementStyleSheet styleSheet = getStyle();
    final StyleKey[] styleKeys = styleSheet.getDefinedPropertyNamesArray();
    for (int i = 0; i < styleKeys.length; i++) {
      final StyleKey styleKey = styleKeys[i];
      if (styleKey != null) {
        target.getStyle().setStyleProperty(styleKey, styleSheet.getStyleProperty(styleKey));
      }
    }

    final Set<Map.Entry<StyleKey, Expression>> styleExpressionEntries =
        getStyleExpressions().entrySet();
    for (final Map.Entry<StyleKey, Expression> entry : styleExpressionEntries) {
      target.setStyleExpression(entry.getKey(), entry.getValue());
    }
  }