/******************************************************************************* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.wink.common.model.atom; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.Providers; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAnyElement; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlMixed; import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; import org.apache.wink.common.RestException; import org.apache.wink.common.internal.i18n.Messages; import org.apache.wink.common.internal.model.AnyContentHandler; import org.apache.wink.common.internal.model.ModelUtils; import org.apache.wink.common.model.synd.SyndContent; /** * The "atom:content" element Per RFC4287 * * <pre> * The "atom:content" element either contains or links to the content of the entry. The content of atom:content is Language-Sensitive. * * atomInlineTextContent = * element atom:content { * atomCommonAttributes, * attribute type { "text" | "html" }?, * (text)* * } * * atomInlineXHTMLContent = * element atom:content { * atomCommonAttributes, * attribute type { "xhtml" }, * xhtmlDiv * } * * atomInlineOtherContent = * element atom:content { * atomCommonAttributes, * attribute type { atomMediaType }?, * (text|anyElement)* * } * * atomOutOfLineContent = * element atom:content { * atomCommonAttributes, * attribute type { atomMediaType }?, * attribute src { atomUri }, * empty * } * * atomContent = atomInlineTextContent * | atomInlineXHTMLContent * | atomInlineOtherContent * | atomOutOfLineContent * * o The "type" Attribute * * On the atom:content element, the value of the "type" attribute MAY be * one of "text", "html", or "xhtml". Failing that, it MUST conform to * the syntax of a MIME media type, but MUST NOT be a composite type * (see Section 4.2.6 of [MIMEREG]). If neither the type attribute nor * the src attribute is provided, Atom Processors MUST behave as though * the type attribute were present with a value of "text". * * o The "src" Attribute * * atom:content MAY have a "src" attribute, whose value MUST be an IRI * reference [RFC3987]. If the "src" attribute is present, atom:content * MUST be empty. Atom Processors MAY use the IRI to retrieve the * content and MAY choose to ignore remote content or to present it in a * different manner than local content. * * If the "src" attribute is present, the "type" attribute SHOULD be * provided and MUST be a MIME media type [MIMEREG], rather than "text", * "html", or "xhtml". The value is advisory; that is to say, when the * corresponding URI (mapped from an IRI, if necessary) is dereferenced, * if the server providing that content also provides a media type, the * server-provided media type is authoritative. * * o Processing Model * * Atom Documents MUST conform to the following rules. Atom Processors * MUST interpret atom:content according to the first applicable rule. * * 1. If the value of "type" is "text", the content of atom:content * MUST NOT contain child elements. Such text is intended to be * presented to humans in a readable fashion. Thus, Atom Processors * MAY collapse white space (including line breaks), and display the * text using typographic techniques such as justification and * proportional fonts. * * 2. If the value of "type" is "html", the content of atom:content * MUST NOT contain child elements and SHOULD be suitable for * handling as HTML [HTML]. The HTML markup MUST be escaped; for * example, "<br>" as "<br>". The HTML markup SHOULD be such * that it could validly appear directly within an HTML <DIV> * element. Atom Processors that display the content MAY use the * markup to aid in displaying it. * * 3. If the value of "type" is "xhtml", the content of atom:content * MUST be a single XHTML div element [XHTML] and SHOULD be suitable * for handling as XHTML. The XHTML div element itself MUST NOT be * considered part of the content. Atom Processors that display the * content MAY use the markup to aid in displaying it. The escaped * versions of characters such as "&" and ">" represent those * characters, not markup. * * 4. If the value of "type" is an XML media type [RFC3023] or ends * with "+xml" or "/xml" (case insensitive), the content of * atom:content MAY include child elements and SHOULD be suitable * for handling as the indicated media type. If the "src" attribute * is not provided, this would normally mean that the "atom:content" * element would contain a single child element that would serve as * the root element of the XML document of the indicated type. * * 5. If the value of "type" begins with "text/" (case insensitive), * the content of atom:content MUST NOT contain child elements. * * 6. For all other values of "type", the content of atom:content MUST * be a valid Base64 encoding, as described in [RFC3548], section 3. * When decoded, it SHOULD be suitable for handling as the indicated * media type. In this case, the characters in the Base64 encoding * MAY be preceded and followed in the atom:content element by white * space, and lines are separated by a single newline (U+000A) * character. * * o Examples * * XHTML inline: * * ... * <content type="xhtml"> * <div xmlns="http://www.w3.org/1999/xhtml"> * This is <b>XHTML</b> content. * </div> * </content> * ... * <content type="xhtml"> * <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"> * This is <xhtml:b>XHTML</xhtml:b> content. * </xhtml:div> * </content> * ... * * The following example assumes that the XHTML namespace has been bound * to the "xh" prefix earlier in the document: * * ... * <content type="xhtml"> * <xh:div> * This is <xh:b>XHTML</xh:b> content. * </xh:div> * </content> * ... * * </pre> */ @XmlAccessorType(XmlAccessType.NONE) @XmlType(name = "atomContent", propOrder = {"any"}) public class AtomContent extends AtomCommonAttributes { @XmlTransient private List<Object> any; @XmlAttribute protected String type; @XmlAttribute protected String src; @XmlTransient private Object savedValue = null; @XmlTransient private Providers providers; public AtomContent() { } public AtomContent(SyndContent value) { super(value); if (value == null) { return; } setSrc(value.getSrc()); setType(value.getType()); // copies the value AS IS without invoking providers setValue(value.getValue(Object.class)); } public SyndContent toSynd(SyndContent value) { if (value == null) { return value; } super.toSynd(value); value.setSrc(getSrc()); value.setType(getType()); // copies the value AS IS without invoking providers value.setValue(getValue(Object.class)); return value; } /** * Sets the Providers on a local field so that the registry of custom and system * providers is available when a client application retrieves the value, expecting * it to be seamlessly unmarshalled or converted to the expected type declared in * getValue(Class). * * Client applications should NOT call this method. */ public void setProviders(Providers _providers) { providers = _providers; } /** * Gets the value of type. */ public String getType() { return type; } /** * Sets the value of type. */ public void setType(String value) { this.type = value; checkValidity(); } /** * Gets the value of src. */ public String getSrc() { return src; } /** * Sets the value of src. */ public void setSrc(String value) { this.src = value; checkValidity(); } /** * Sets the content of the "atom:content" element as a String. The "type" * attribute should be set prior to setting the contents. * <p> * Atom Documents MUST conform to the following rules. Atom Processors MUST * interpret atom:content according to the first applicable rule. * </p> * <ol> * <li>If the value of "type" is "text", the content of atom:content MUST * NOT contain child elements. Such text is intended to be presented to * humans in a readable fashion. Thus, Atom Processors MAY collapse white * space (including line breaks), and display the text using typographic * techniques such as justification and proportional fonts. * <li> * <li>If the value of "type" is "html", the content of atom:content MUST * NOT contain child elements and SHOULD be suitable for handling as HTML * [HTML]. The HTML markup MUST be escaped; for example, "<br> * " as "<br>". The HTML markup SHOULD be such that it could validly * appear directly within an HTML <DIV> element. Atom Processors that * display the content MAY use the markup to aid in displaying it. * <li> * <li>If the value of "type" is "xhtml", the content of atom:content MUST * be a single XHTML div element [XHTML] and SHOULD be suitable for handling * as XHTML. The XHTML div element itself MUST NOT be considered part of the * content. Atom Processors that display the content MAY use the markup to * aid in displaying it. The escaped versions of characters such as "&" and * ">" represent those characters, not markup. * <li> * <li>If the value of "type" is an XML media type [RFC3023] or ends with * "+xml" or "/xml" (case insensitive), the content of atom:content MAY * include child elements and SHOULD be suitable for handling as the * indicated media type. If the "src" attribute is not provided, this would * normally mean that the "atom:content" element would contain a single * child element that would serve as the root element of the XML document of * the indicated type. * <li> * <li>If the value of "type" begins with "text/" (case insensitive), the * content of atom:content MUST NOT contain child elements. * <li> * <li>For all other values of "type", the content of atom:content MUST be a * valid Base64 encoding, as described in [RFC3548], section 3. When * decoded, it SHOULD be suitable for handling as the indicated media type. * In this case, the characters in the Base64 encoding MAY be preceded and * followed in the atom:content element by white space, and lines are * separated by a single newline (U+000A) character. * <li> * </ol> */ public void setValue(Object value) { if (value != null) { any = Arrays.asList(value); } else { any = null; } checkValidity(); } /** * <p> * Gets the content of the "atom:content" element as a String. The "type" * attribute should be used to determine how to treat the content. * <p> * Pay attention that de-serialization occurs each time the method is * called, so multiple calls to this method may effect the application * performance. */ public String getValue() { return getValue(String.class); } /** * <p> * Gets the content of the "atom:content" element serialized to provided * class. The "type" attribute should be used to determine how to treat the * content. * <p> * Pay attention that de-serialization occurs each time the method is * called, so multiple calls to this method may effect the application * performance. */ public <T> T getValue(Class<T> cls) { try { return getValue(cls, cls, providers, ModelUtils.EMPTY_ARRAY, ModelUtils.EMPTY_STRING_MAP, ModelUtils.determineMediaType(type)); } catch (IOException e) { // should never happen throw new WebApplicationException(e); } } /** * <p> * Gets the content of the "atom:content" element serialized to provided * class according to provided parameters. * <p> * Pay attention that de-serialization occurs each time the method is * called, so multiple calls to this method may effect the application * performance. */ public <T> T getValue(Class<T> cls, Type genericType, Providers providers, Annotation[] annotations, MultivaluedMap<String, String> httpHeaders, MediaType mediaType) throws IOException { return ModelUtils.readValue(getAny(), cls, providers, genericType, annotations, httpHeaders, mediaType); } @XmlMixed @XmlAnyElement(lax = true, value = AnyContentHandler.class) List<Object> getAny() { ModelUtils.fixAnyContent(any, type); return any; } void setAny(List<Object> any) { this.any = any; } public void checkValidity() { if (src != null && any != null) { throw new RestException(Messages.getMessage("contentMayHaveInlineOrOutContent")); //$NON-NLS-1$ } else if (src != null && type != null) { if (type.equals("text") || type.equals("html") || type.equals("xhtml")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ throw new RestException( Messages.getMessage("typeAttribMustHaveValidMimeType")); //$NON-NLS-1$ } } } /* package */void revertValue() { setValue(savedValue); savedValue = null; } /* package */Object saveValue() { this.savedValue = getValue(); setValue(null); return this.savedValue; } }