package org.jboss.resteasy.plugins.providers.atom; import org.jboss.resteasy.plugins.providers.jaxb.JAXBContextFinder; import org.jboss.resteasy.plugins.providers.jaxb.JAXBXmlTypeProvider; import org.w3c.dom.Element; import javax.ws.rs.core.MediaType; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAnyElement; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementRef; import javax.xml.bind.annotation.XmlMixed; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; import java.net.URI; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * <p>Per RFC4287:</p> * <pre> * The "atom:entry" element represents an individual entry, acting as a * container for metadata and data associated with the entry. This * element can appear as a child of the atom:feed element, or it can * appear as the document (i.e., top-level) element of a stand-alone * Atom Entry Document. * <p/> * atomEntry = * element atom:entry { * atomCommonAttributes, * (atomAuthor* * & atomCategory* * & atomContent? * & atomContributor* * & atomId * & atomLink* * & atomPublished? * & atomRights? * & atomSource? * & atomSummary? * & atomTitle * & atomUpdated * & extensionElement*) * } * <p/> * This specification assigns no significance to the order of appearance * of the child elements of atom:entry. * <p/> * The following child elements are defined by this specification (note * that it requires the presence of some of these elements): * <p/> * o atom:entry elements MUST contain one or more atom:author elements, * unless the atom:entry contains an atom:source element that * contains an atom:author element or, in an Atom Feed Document, the * atom:feed element contains an atom:author element itself. * o atom:entry elements MAY contain any number of atom:category * elements. * o atom:entry elements MUST NOT contain more than one atom:content * element. * o atom:entry elements MAY contain any number of atom:contributor * elements. * o atom:entry elements MUST contain exactly one atom:id element. * o atom:entry elements that contain no child atom:content element * MUST contain at least one atom:link element with a rel attribute * value of "alternate". * o atom:entry elements MUST NOT contain more than one atom:link * element with a rel attribute value of "alternate" that has the * same combination of type and hreflang attribute values. * o atom:entry elements MAY contain additional atom:link elements * beyond those described above. * o atom:entry elements MUST NOT contain more than one atom:published * element. * o atom:entry elements MUST NOT contain more than one atom:rights * element. * o atom:entry elements MUST NOT contain more than one atom:source * element. * o atom:entry elements MUST contain an atom:summary element in either * of the following cases: * * the atom:entry contains an atom:content that has a "src" * attribute (and is thus empty). * * the atom:entry contains content that is encoded in Base64; * i.e., the "type" attribute of atom:content is a MIME media type * [MIMEREG], but is not an XML media type [RFC3023], does not * begin with "text/", and does not end with "/xml" or "+xml". * o atom:entry elements MUST NOT contain more than one atom:summary * element. * o atom:entry elements MUST contain exactly one atom:title element. * o atom:entry elements MUST contain exactly one atom:updated element. * </pre> * * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ @XmlRootElement(name = "entry") @XmlAccessorType(XmlAccessType.PROPERTY) @XmlType(propOrder = {"titleElement", "links", "categories", "updated", "id", "published", "authors", "contributors", "source", "rightsElement", "content", "summaryElement", "anyOther"}) public class Entry extends CommonAttributes { private List<Person> authors = new ArrayList<Person>(); private List<Category> categories = new ArrayList<Category>(); private Content content; private List<Person> contributors = new ArrayList<Person>(); private URI id; private List<Link> links = new ArrayList<Link>(); private Date published; private Text title; private Date updated; private Text rights; private Source source; private Text summary; private Element anyOtherElement; private List<Object> anyOther; private Object anyOtherJaxbObject; protected JAXBContextFinder finder; protected void setFinder(JAXBContextFinder finder) { this.finder = finder; } @XmlElement public URI getId() { return id; } public void setId(URI id) { this.id = id; } @XmlElement(name = "title") public Text getTitleElement() { return title; } public void setTitleElement(Text title) { this.title = title; } @XmlTransient public String getTitle() { if (this.title == null) { return null; } return title.getText(); } public void setTitle(String title) { if (this.title == null) { this.title = new Text(); } this.title.setText(title); } @XmlElement public Date getUpdated() { return updated; } public void setUpdated(Date updated) { this.updated = updated; } public Link getLinkByRel(String name) { for (Link link : links) if (link.getRel().equals(name)) return link; return null; } @XmlElementRef public List<Link> getLinks() { return links; } @XmlElementRef public Content getContent() { return content; } public void setContent(Content content) { this.content = content; } @XmlElement(name = "author") public List<Person> getAuthors() { return authors; } @XmlElementRef public List<Category> getCategories() { return categories; } @XmlElement(name = "contributor") public List<Person> getContributors() { return contributors; } @XmlElement public Date getPublished() { return published; } public void setPublished(Date published) { this.published = published; } @XmlElement(name = "rights") public Text getRightsElement() { return rights; } public void setRightsElement(Text rights) { this.rights = rights; } @XmlTransient public String getRights() { if (rights == null) { return null; } return rights.getText(); } public void setRights(String rights) { if (this.rights == null) { this.rights = new Text(); } this.rights.setText(rights); } @XmlElement public Source getSource() { return source; } public void setSource(Source source) { this.source = source; } @XmlElement(name = "summary") public Text getSummaryElement() { return summary; } public void setSummaryElement(Text summary) { this.summary = summary; } @XmlTransient public String getSummary() { if (rights == null) { return null; } return rights.getText(); } public void setSummary(String summary) { if (this.summary == null) { this.summary = new Text(); } this.summary.setText(summary); } /** * Get content as an XML Element if the content is XML. Otherwise, this will just return null. * * @return */ @XmlTransient public Element getAnyOtherElement() { if (anyOther == null) return null; if (anyOtherElement != null) return anyOtherElement; for (Object obj : anyOther) { if (obj instanceof Element) { anyOtherElement = (Element) obj; return anyOtherElement; } } return null; } @XmlMixed @XmlAnyElement(lax = true) public List<Object> getAnyOther() { if (anyOther == null) { anyOther = new ArrayList<Object>(); } return this.anyOther; } /** * Extract the content as the provided JAXB annotated type. * <p/> * This method will use a cached JAXBContext used by the Resteasy JAXB providers * or, if those are not existent, it will create a new JAXBContext from scratch * using the class. * * @param clazz class type you are expecting * @param otherPossibleClasses Other classe you want to create the JAXBContext with * @return null if there is no XML content * @throws JAXBException */ @SuppressWarnings({ "unchecked", "rawtypes" }) public <T> T getAnyOtherJAXBObject(Class<T> clazz, Class... otherPossibleClasses) throws JAXBException { JAXBContext ctx = null; Class[] classes = {clazz}; if (otherPossibleClasses != null && otherPossibleClasses.length > 0) { classes = new Class[1 + otherPossibleClasses.length]; classes[0] = clazz; for (int i = 0; i < otherPossibleClasses.length; i++) classes[i + 1] = otherPossibleClasses[i]; } if (finder != null) { ctx = finder.findCacheContext(MediaType.APPLICATION_XML_TYPE, null, classes); } else { ctx = JAXBContext.newInstance(classes); } Object obj = null; if (getAnyOtherElement() != null) { obj = ctx.createUnmarshaller().unmarshal(getAnyOtherElement()); } else { if (getAnyOther().size() == 0) return null; for (Object _obj : getAnyOther()) { for (Class _clazz : classes) { if (_obj.getClass().equals(_clazz)) { obj = _obj; break; } } } if (obj == null) return null; } if (obj instanceof JAXBElement) { anyOtherJaxbObject = ((JAXBElement) obj).getValue(); return (T) anyOtherJaxbObject; } else { anyOtherJaxbObject = obj; return (T) obj; } } /** * Returns previous extracted jaxbobject from a call to getJAXBObject(Class<T> clazz) * or value passed in through a previous setJAXBObject(). * * @return */ @XmlTransient public Object getAnyOtherJAXBObject() { return anyOtherJaxbObject; } public void setAnyOtherJAXBObject(Object obj) { if (anyOther == null) anyOther = new ArrayList<Object>(); if (anyOtherJaxbObject != null && anyOther != null) anyOther.clear(); if (!obj.getClass().isAnnotationPresent(XmlRootElement.class) && obj.getClass().isAnnotationPresent(XmlType.class)) { anyOther.add(JAXBXmlTypeProvider.wrapInJAXBElement(obj, obj.getClass())); } else { anyOther.add(obj); } anyOtherJaxbObject = obj; } }