class AbstractComplexProperty extends AbstractField
{

    private final ComplexPropertyContainer container;

    private final Map<String, String> namespaceToPrefix;

    public AbstractComplexProperty(XMPMetadata metadata, String propertyName)
    {
        super(metadata, propertyName);
        container = new ComplexPropertyContainer();
        this.namespaceToPrefix = new HashMap<>();
    }

    public void addNamespace(String namespace, String prefix)
    {
        this.namespaceToPrefix.put(namespace, prefix);
    }

    public String getNamespacePrefix(String namespace)
    {
        return this.namespaceToPrefix.get(namespace);
    }

    public Map<String, String> getAllNamespacesWithPrefix()
    {
        return this.namespaceToPrefix;
    }

    /**
     * Add a property to the current structure
     * 
     * @param obj
     *            the property to add
     */
    public final void addProperty(AbstractField obj)
    {<FILL_FUNCTION_BODY>}

    /**
     * Remove a property
     * 
     * @param property
     *            The property to remove
     */
    public final void removeProperty(AbstractField property)
    {
        container.removeProperty(property);
    }

    // /**
    // * Return the container of this Array
    // *
    // * @return The complex property container that represents content of this
    // * property
    // */
    public final ComplexPropertyContainer getContainer()
    {
        return container;
    }

    public final List<AbstractField> getAllProperties()
    {
        return container.getAllProperties();
    }

    public final AbstractField getProperty(String fieldName)
    {
        List<AbstractField> list = container.getPropertiesByLocalName(fieldName);
        // return null if no property
        if (list == null)
        {
            return null;
        }
        // return the first element of the list
        return list.get(0);
    }

    public final ArrayProperty getArrayProperty(String fieldName)
    {
        List<AbstractField> list = container.getPropertiesByLocalName(fieldName);
        // return null if no property
        if (list == null)
        {
            return null;
        }
        // return the first element of the list
        return (ArrayProperty) list.get(0);
    }

    protected final AbstractField getFirstEquivalentProperty(String localName, Class<? extends AbstractField> type)
    {
        return container.getFirstEquivalentProperty(localName, type);
    }

}

        // https://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/cs6/XMPSpecificationPart1.pdf
        // "Each property name in an XMP packet shall be unique within that packet"
        // "Multiple values are represented using an XMP array value"
        // "The nested element's element content shall consist of zero or more rdf:li elements, 
        // one for each item in the array"
        // thus delete existing elements of a property, except for arrays ("li")
        if (!(this instanceof ArrayProperty))
        {
            container.removePropertiesByName(obj.getPropertyName());
        }
        container.addProperty(obj);
    
<methods>public void <init>(org.apache.xmpbox.XMPMetadata, java.lang.String) ,public final boolean containsAttribute(java.lang.String) ,public final List<org.apache.xmpbox.type.Attribute> getAllAttributes() ,public final org.apache.xmpbox.type.Attribute getAttribute(java.lang.String) ,public final org.apache.xmpbox.XMPMetadata getMetadata() ,public abstract java.lang.String getNamespace() ,public abstract java.lang.String getPrefix() ,public final java.lang.String getPropertyName() ,public final void removeAttribute(java.lang.String) ,public final void setAttribute(org.apache.xmpbox.type.Attribute) ,public final void setPropertyName(java.lang.String) <variables>private final non-sealed Map<java.lang.String,org.apache.xmpbox.type.Attribute> attributes,private final non-sealed org.apache.xmpbox.XMPMetadata metadata,private java.lang.String propertyName