/**
* Copyright Intellectual Reserve, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gedcomx.common;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.gedcomx.rt.SupportsExtensionElements;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import java.util.*;
/**
* A set of data that supports extension elements.
*
* @author Ryan Heaton
*/
@XmlType( name = "ExtensibleData" )
public abstract class ExtensibleData implements SupportsExtensionElements, HasTransientProperties {
private String id;
protected List<Object> extensionElements;
protected final Map<String, Object> transientProperties = new TreeMap<String, Object>();
/**
* A local, context-specific id for the data.
*
* @return A local, context-specific id for the data.
*/
@XmlAttribute
public String getId() {
return id;
}
/**
* A local, context-specific id for the data.
*
* @param id A local, context-specific id for the data.
*/
public void setId(String id) {
this.id = id;
}
/**
* Build up this object with an id.
*
* @param id The id.
* @return this.
*/
public ExtensibleData id(String id) {
this.id = id;
return this;
}
/**
* Custom extension elements for a conclusion.
*
* @return Custom extension elements for a conclusion.
*/
@XmlAnyElement (lax = true)
@JsonIgnore @org.codehaus.jackson.annotate.JsonIgnore
public List<Object> getExtensionElements() {
return extensionElements;
}
/**
* Custom extension elements for a conclusion.
*
* @param extensionElements Custom extension elements for a conclusion.
*/
@JsonIgnore @org.codehaus.jackson.annotate.JsonIgnore
public void setExtensionElements(List<Object> extensionElements) {
this.extensionElements = extensionElements;
}
/**
* Add an extension element.
*
* @param element The extension element to add.
*/
public void addExtensionElement(Object element) {
if (this.extensionElements == null) {
this.extensionElements = new ArrayList<Object>();
}
this.extensionElements.add(element);
}
public ExtensibleData extensionElement(Object element) {
addExtensionElement(element);
return this;
}
/**
* Remove extension elements of a given type.
*
* @param clazz The type of extension element to remove.
* @param <E> The type of extension elements.
* @return The removed extension elements.
*/
@SuppressWarnings ( {"unchecked"} )
public <E> List<E> removeExtensionElements(Class<E> clazz) {
List<E> removed = new ArrayList<E>();
if (this.extensionElements != null) {
Iterator<Object> elements = this.extensionElements.iterator();
while (elements.hasNext()) {
E next = (E) elements.next();
if (clazz.isInstance(next)) {
removed.add(next);
elements.remove();
}
}
}
return removed;
}
/**
* Sets an extension element by first removing all previous elements of the same type, then adding it to the list.
*
* @param element The element to set.
*/
public void setExtensionElement(Object element) {
removeExtensionElements(element.getClass());
addExtensionElement(element);
}
/**
* Finds the first extension of a specified type.
*
* @param clazz The type.
* @param <E> The type of extension elements.
* @return The extension, or null if none found.
*/
@SuppressWarnings ( {"unchecked"} )
public <E> E findExtensionOfType(Class<E> clazz) {
List<E> candidates = findExtensionsOfType(clazz);
if (candidates.size() > 0) {
return candidates.get(0);
}
return null;
}
/**
* Find the extensions of a specified type.
*
* @param clazz The type.
* @param <E> The type.
* @return The extensions, possibly empty but not null.
*/
@SuppressWarnings ( {"unchecked"} )
public <E> List<E> findExtensionsOfType(Class<E> clazz) {
List<E> ext = new ArrayList<E>();
if (this.extensionElements != null) {
for (Object extension : extensionElements) {
if (clazz.isInstance(extension)) {
ext.add((E) extension);
}
}
}
return ext;
}
/**
* Finds the first extension of a specified type in the given name and namespace.
*
* @param clazz The type.
* @param name The name of the extension element.
* @param namespace The namespace of the extension element.
* @return The extension, or null if none found.
*/
@SuppressWarnings ( {"unchecked"} )
public <E> E findExtensionOfType(Class<E> clazz, String name, String namespace) {
List<E> candidates = findExtensionsOfType(clazz, name, namespace);
if (candidates.size() > 0) {
return candidates.get(0);
}
return null;
}
/**
* Find the extension elements of a specified type in the given name and namespace.
*
* @param clazz The type of the extension element.
* @param name The name of the extension element.
* @param namespace The namespace of the extension element.
* @return The extensions, possibly empty but not null.
*/
@SuppressWarnings ( {"unchecked"} )
public <E> List<E> findExtensionsOfType(Class<E> clazz, String name, String namespace) {
List<E> ext = new ArrayList<E>();
if (this.extensionElements != null) {
for (Object extension : extensionElements) {
if (JAXBElement.class.isInstance(extension)) {
JAXBElement<E> element = (JAXBElement<E>) extension;
if (clazz.isInstance(element.getValue()) && element.getName().getLocalPart().equals(name) && element.getName().getNamespaceURI().equals(namespace)) {
ext.add(element.getValue());
}
}
}
}
return ext;
}
/**
* Get the transient properties.
*
* @return the transient properties.
*/
@JsonIgnore @org.codehaus.jackson.annotate.JsonIgnore
@XmlTransient
@Override
public Map<String, Object> getTransientProperties() {
return Collections.unmodifiableMap(transientProperties);
}
/**
* Get a transient (non-serialized) property.
*
* @param name The name of the property.
* @return The property.
*/
@Override
public Object getTransientProperty(String name) {
return this.transientProperties.get(name);
}
/**
* Set a transient (non-serialized) property.
*
* @param name the name of the property.
* @param value the property value.
*/
@Override
public void setTransientProperty(String name, Object value) {
this.transientProperties.put(name, value);
}
protected void embed(ExtensibleData data) {
if (data.extensionElements != null) {
this.extensionElements = this.extensionElements == null ? new ArrayList<Object>() : this.extensionElements;
this.extensionElements.addAll(data.extensionElements);
}
}
/**
* Provide a simple toString() method.
*/
@Override
public String toString() {
return "id: " + id;
}
}