org.apache.juneau.html.HtmlSerializer Maven / Gradle / Ivy
// ***************************************************************************************************************************
// * 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.juneau.html;
import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.serializer.*;
import org.apache.juneau.xml.*;
/**
* Serializes POJO models to HTML.
*
* Media types:
*
* Handles Accept
types: text/html
*
*
* Produces Content-Type
types: text/html
*
*
Description:
*
* The conversion is as follows...
*
* -
* {@link Map Maps} (e.g. {@link HashMap}, {@link TreeMap}) and beans are converted to HTML tables with
* 'key' and 'value' columns.
*
-
* {@link Collection Collections} (e.g. {@link HashSet}, {@link LinkedList}) and Java arrays are converted
* to HTML ordered lists.
*
-
* {@code Collections} of {@code Maps} and beans are converted to HTML tables with keys as headers.
*
-
* Everything else is converted to text.
*
*
*
* This serializer provides several serialization options. Typically, one of the predefined DEFAULT
* serializers will be sufficient.
* However, custom serializers can be constructed to fine-tune behavior.
*
*
* The {@link HtmlLink} annotation can be used on beans to add hyperlinks to the output.
*
*
Behavior-specific subclasses
*
* The following direct subclasses are provided for convenience:
*
* -
* {@link Sq} - Default serializer, single quotes.
*
-
* {@link SqReadable} - Default serializer, single quotes, whitespace added.
*
*
* Example:
*
* // Use one of the default serializers to serialize a POJO
* String html = HtmlSerializer.DEFAULT .serialize(someObject);
*
* // Create a custom serializer that doesn't use whitespace and newlines
* HtmlSerializer serializer = new HtmlSerializerBuider().ws().build();
*
* // Same as above, except uses cloning
* HtmlSerializer serializer = HtmlSerializer.DEFAULT .builder().ws().build();
*
* // Serialize POJOs to HTML
*
* // Produces:
* // <ul><li>1<li>2<li>3</ul>
* List l = new ObjectList(1, 2, 3);
* String html = HtmlSerializer.DEFAULT .serialize(l);
*
* // Produces:
* // <table>
* // <tr><th>firstName</th><th>lastName</th></tr>
* // <tr><td>Bob</td><td>Costas</td></tr>
* // <tr><td>Billy</td><td>TheKid</td></tr>
* // <tr><td>Barney</td><td>Miller</td></tr>
* // </table>
* l = new ObjectList();
* l.add(new ObjectMap("{firstName:'Bob',lastName:'Costas'}" ));
* l.add(new ObjectMap("{firstName:'Billy',lastName:'TheKid'}" ));
* l.add(new ObjectMap("{firstName:'Barney',lastName:'Miller'}" ));
* String html = HtmlSerializer.DEFAULT .serialize(l);
*
* // Produces:
* // <table>
* // <tr><th>key</th><th>value</th></tr>
* // <tr><td>foo</td><td>bar</td></tr>
* // <tr><td>baz</td><td>123</td></tr>
* // </table>
* Map m = new ObjectMap("{foo:'bar',baz:123}" );
* String html = HtmlSerializer.DEFAULT .serialize(m);
*
* // HTML elements can be nested arbitrarily deep
* // Produces:
* // <table>
* // <tr><th>key</th><th>value</th></tr>
* // <tr><td>foo</td><td>bar</td></tr>
* // <tr><td>baz</td><td>123</td></tr>
* // <tr><td>someNumbers</td><td><ul><li>1<li>2<li>3</ul></td></tr>
* // <tr><td>someSubMap</td><td>
* // <table>
* // <tr><th>key</th><th>value</th></tr>
* // <tr><td>a</td><td>b</td></tr>
* // </table>
* // </td></tr>
* // </table>
* Map m = new ObjectMap("{foo:'bar',baz:123}" );
* m.put("someNumbers", new ObjectList(1, 2, 3));
* m.put("someSubMap" , new ObjectMap("{a:'b'}" ));
* String html = HtmlSerializer.DEFAULT .serialize(m);
*
*/
public class HtmlSerializer extends XmlSerializer {
//-------------------------------------------------------------------------------------------------------------------
// Configurable properties
//-------------------------------------------------------------------------------------------------------------------
private static final String PREFIX = "HtmlSerializer.";
/**
* Configuration property: Anchor text source.
*
*
* - Name:
"HtmlSerializer.uriAnchorText"
* - Data type:
AnchorText
* - Default:
TO_STRING
* - Session-overridable:
true
*
*
*
* When creating anchor tags (e.g. <a href ='...'
* > text</a>
) in HTML, this setting defines what to set the inner text to.
*
*
* See the {@link AnchorText} enum for possible values.
*/
public static final String HTML_uriAnchorText = PREFIX + "uriAnchorText";
/**
* Configuration property: Look for URLs in {@link String Strings}.
*
*
* - Name:
"HtmlSerializer.detectLinksInStrings"
* - Data type:
Boolean
* - Default:
true
* - Session-overridable:
true
*
*
*
* If a string looks like a URL (e.g. starts with "http://" or "https://" , then treat it like a URL
* and make it into a hyperlink based on the rules specified by {@link #HTML_uriAnchorText}.
*/
public static final String HTML_detectLinksInStrings = PREFIX + "detectLinksInStrings";
/**
* Configuration property: Look for link labels in the "label" parameter of the URL.
*
*
* - Name:
"HtmlSerializer.lookForLabelParameters"
* - Data type:
Boolean
* - Default:
true
* - Session-overridable:
true
*
*
*
* If the URL has a label parameter (e.g. "?label=foobar" ), then use that as the anchor text of the link.
*
*
* The parameter name can be changed via the {@link #HTML_labelParameter} property.
*/
public static final String HTML_lookForLabelParameters = PREFIX + "lookForLabelParameters";
/**
* Configuration property: The parameter name to use when using {@link #HTML_lookForLabelParameters}.
*
*
* - Name:
"HtmlSerializer.labelParameter"
* - Data type:
String
* - Default:
"label"
* - Session-overridable:
true
*
*/
public static final String HTML_labelParameter = PREFIX + "labelParameter";
/**
* Configuration property: Add key/value headers on bean/map tables.
*
*
* - Name:
"HtmlSerializer.addKeyValueTableHeaders"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*/
public static final String HTML_addKeyValueTableHeaders = PREFIX + "addKeyValueTableHeaders";
/**
* Configuration property: Add "_type" properties when needed.
*
*
* - Name:
"HtmlSerializer.addBeanTypeProperties"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*
*
* If true , then "_type" properties will be added to beans if their type cannot be inferred
* through reflection.
* This is used to recreate the correct objects during parsing if the object types cannot be inferred.
* For example, when serializing a {@code Map} field, where the bean class cannot be determined
* from the value type.
*
*
* When present, this value overrides the {@link #SERIALIZER_addBeanTypeProperties} setting and is
* provided to customize the behavior of specific serializers in a {@link SerializerGroup}.
*/
public static final String HTML_addBeanTypeProperties = PREFIX + "addBeanTypeProperties";
//-------------------------------------------------------------------------------------------------------------------
// Predefined instances
//-------------------------------------------------------------------------------------------------------------------
/** Default serializer, all default settings. */
public static final HtmlSerializer DEFAULT = new HtmlSerializer(PropertyStore.create());
/** Default serializer, single quotes. */
public static final HtmlSerializer DEFAULT_SQ = new HtmlSerializer.Sq(PropertyStore.create());
/** Default serializer, single quotes, whitespace added. */
public static final HtmlSerializer DEFAULT_SQ_READABLE = new HtmlSerializer.SqReadable(PropertyStore.create());
//-------------------------------------------------------------------------------------------------------------------
// Predefined subclasses
//-------------------------------------------------------------------------------------------------------------------
/** Default serializer, single quotes. */
public static class Sq extends HtmlSerializer {
/**
* Constructor.
*
* @param propertyStore The property store containing all the settings for this object.
*/
public Sq(PropertyStore propertyStore) {
super(propertyStore.copy().append(SERIALIZER_quoteChar, '\''));
}
}
/** Default serializer, single quotes, whitespace added. */
public static class SqReadable extends HtmlSerializer {
/**
* Constructor.
*
* @param propertyStore The property store containing all the settings for this object.
*/
public SqReadable(PropertyStore propertyStore) {
super(propertyStore.copy().append(SERIALIZER_quoteChar, '\'').append(SERIALIZER_useWhitespace, true));
}
}
//-------------------------------------------------------------------------------------------------------------------
// Instance
//-------------------------------------------------------------------------------------------------------------------
final HtmlSerializerContext ctx;
private volatile HtmlSchemaDocSerializer schemaSerializer;
/**
* Constructor.
*
* @param propertyStore
* The property store containing all the settings for this object.
*/
public HtmlSerializer(PropertyStore propertyStore) {
this(propertyStore, "text/html");
}
/**
* Constructor.
*
* @param propertyStore
* The property store containing all the settings for this object.
* @param produces
* The media type that this serializer produces.
* @param accept
* The accept media types that the serializer can handle.
*
* Can contain meta-characters per the media-type
specification of
* RFC2616/14.1
*
* If empty, then assumes the only media type supported is produces
.
*
* For example, if this serializer produces "application/json" but should handle media types of
* "application/json" and "text/json" , then the arguments should be:
*
super (propertyStore, "application/json" , "application/json" , "text/json" );
*
...or...
*
super (propertyStore, "application/json" , "*/json" );
*/
public HtmlSerializer(PropertyStore propertyStore, String produces, String...accept) {
super(propertyStore, produces, accept);
this.ctx = createContext(HtmlSerializerContext.class);
}
@Override /* CoreObject */
public HtmlSerializerBuilder builder() {
return new HtmlSerializerBuilder(propertyStore);
}
@Override /* XmlSerializer */
public HtmlSerializer getSchemaSerializer() {
if (schemaSerializer == null)
schemaSerializer = new HtmlSchemaDocSerializer(propertyStore);
return schemaSerializer;
}
@Override /* Serializer */
public WriterSerializerSession createSession(SerializerSessionArgs args) {
return new HtmlSerializerSession(ctx, args);
}
}