http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java index 12f3fb6..28a895a 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java @@ -12,20 +12,9 @@ // *************************************************************************************************************************** package org.apache.juneau.html; -import static javax.xml.stream.XMLStreamConstants.*; -import static org.apache.juneau.html.HtmlTag.*; -import static org.apache.juneau.internal.StringUtils.*; - -import java.lang.reflect.*; -import java.util.*; - -import javax.xml.stream.*; - import org.apache.juneau.*; import org.apache.juneau.annotation.*; -import org.apache.juneau.http.*; import org.apache.juneau.parser.*; -import org.apache.juneau.transform.*; import org.apache.juneau.xml.*; /** @@ -48,7 +37,7 @@ import org.apache.juneau.xml.*; * <li>{@link HtmlSerializerContext} * </ul> */ -@SuppressWarnings({ "rawtypes", "unchecked", "hiding" }) +@SuppressWarnings({ "hiding" }) @Consumes("text/html,text/html+stripped") public class HtmlParser extends XmlParser { @@ -73,529 +62,8 @@ public class HtmlParser extends XmlParser { return new HtmlParserBuilder(propertyStore); } - /* - * Reads anything starting at the current event. - * <p> - * Precondition: Must be pointing at outer START_ELEMENT. - * Postcondition: Pointing at outer END_ELEMENT. - */ - private <T> T parseAnything(HtmlParserSession session, ClassMeta<T> eType, XMLStreamReader r, Object outer, - boolean isRoot, BeanPropertyMeta pMeta) throws Exception { - - if (eType == null) - eType = (ClassMeta<T>)object(); - PojoSwap<T,Object> transform = (PojoSwap<T,Object>)eType.getPojoSwap(); - ClassMeta<?> sType = eType.getSerializedClassMeta(); - session.setCurrentClass(sType); - - int event = r.getEventType(); - if (event != START_ELEMENT) - throw new XmlParseException(r.getLocation(), "parseAnything must be called on outer start element."); - - if (! isRoot) - event = r.next(); - boolean isEmpty = (event == END_ELEMENT); - - // Skip until we find a start element, end document, or non-empty text. - if (! isEmpty) - event = skipWs(r); - - if (event == END_DOCUMENT) - throw new XmlParseException(r.getLocation(), "Unexpected end of stream in parseAnything for type ''{0}''", eType); - - // Handle @Html(asXml=true) beans. - HtmlClassMeta hcm = sType.getExtendedMeta(HtmlClassMeta.class); - if (hcm.isAsXml()) - return super.parseAnything(session, eType, null, r, outer, false, pMeta); - - Object o = null; - - boolean isValid = true; - HtmlTag tag = (event == CHARACTERS ? null : HtmlTag.forString(r.getName().getLocalPart(), false)); - - if (tag == HTML) - tag = skipToData(r); - - if (isEmpty) { - o = ""; - } else if (tag == null || tag.isOneOf(BR,BS,FF,SP)) { - String text = session.parseText(r); - if (sType.isObject() || sType.isCharSequence()) - o = text; - else if (sType.isChar()) - o = text.charAt(0); - else if (sType.isBoolean()) - o = Boolean.parseBoolean(text); - else if (sType.isNumber()) - o = parseNumber(text, (Class<? extends Number>)eType.getInnerClass()); - else if (sType.canCreateNewInstanceFromString(outer)) - o = sType.newInstanceFromString(outer, text); - else if (sType.canCreateNewInstanceFromNumber(outer)) - o = sType.newInstanceFromNumber(session, outer, parseNumber(text, sType.getNewInstanceFromNumberClass())); - else - isValid = false; - - } else if (tag == STRING || (tag == A && pMeta != null - && pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).getLink() != null)) { - String text = session.getElementText(r); - if (sType.isObject() || sType.isCharSequence()) - o = text; - else if (sType.isChar()) - o = text.charAt(0); - else if (sType.canCreateNewInstanceFromString(outer)) - o = sType.newInstanceFromString(outer, text); - else if (sType.canCreateNewInstanceFromNumber(outer)) - o = sType.newInstanceFromNumber(session, outer, parseNumber(text, sType.getNewInstanceFromNumberClass())); - else - isValid = false; - skipTag(r, tag == STRING ? xSTRING : xA); - - } else if (tag == NUMBER) { - String text = session.getElementText(r); - if (sType.isObject()) - o = parseNumber(text, Number.class); - else if (sType.isNumber()) - o = parseNumber(text, (Class<? extends Number>)sType.getInnerClass()); - else if (sType.canCreateNewInstanceFromNumber(outer)) - o = sType.newInstanceFromNumber(session, outer, parseNumber(text, sType.getNewInstanceFromNumberClass())); - else - isValid = false; - skipTag(r, xNUMBER); - - } else if (tag == BOOLEAN) { - String text = session.getElementText(r); - if (sType.isObject() || sType.isBoolean()) - o = Boolean.parseBoolean(text); - else - isValid = false; - skipTag(r, xBOOLEAN); - - } else if (tag == P) { - String text = session.getElementText(r); - if (! "No Results".equals(text)) - isValid = false; - skipTag(r, xP); - - } else if (tag == NULL) { - skipTag(r, NULL); - skipTag(r, xNULL); - - } else if (tag == A) { - o = parseAnchor(session, r, eType); - skipTag(r, xA); - - } else if (tag == TABLE) { - - String typeName = getAttribute(r, session.getBeanTypePropertyName(eType), "object"); - ClassMeta cm = session.getClassMeta(typeName, pMeta, eType); - - if (cm != null) { - sType = eType = cm; - typeName = sType.isArray() ? "array" : "object"; - } else if (! "array".equals(typeName)) { - // Type name could be a subtype name. - typeName = "object"; - } - - if (typeName.equals("object")) { - if (sType.isObject()) { - o = parseIntoMap(session, r, (Map)new ObjectMap(session), sType.getKeyType(), sType.getValueType(), - pMeta); - } else if (sType.isMap()) { - o = parseIntoMap(session, r, (Map)(sType.canCreateNewInstance(outer) ? sType.newInstance(outer) - : new ObjectMap(session)), sType.getKeyType(), sType.getValueType(), pMeta); - } else if (sType.canCreateNewBean(outer)) { - BeanMap m = session.newBeanMap(outer, sType.getInnerClass()); - o = parseIntoBean(session, r, m).getBean(); - } else { - isValid = false; - } - skipTag(r, xTABLE); - - } else if (typeName.equals("array")) { - if (sType.isObject()) - o = parseTableIntoCollection(session, r, (Collection)new ObjectList(session), sType, pMeta); - else if (sType.isCollection()) - o = parseTableIntoCollection(session, r, (Collection)(sType.canCreateNewInstance(outer) - ? sType.newInstance(outer) : new ObjectList(session)), sType, pMeta); - else if (sType.isArray() || sType.isArgs()) { - ArrayList l = (ArrayList)parseTableIntoCollection(session, r, new ArrayList(), sType, pMeta); - o = session.toArray(sType, l); - } - else - isValid = false; - skipTag(r, xTABLE); - - } else { - isValid = false; - } - - } else if (tag == UL) { - String typeName = getAttribute(r, session.getBeanTypePropertyName(eType), "array"); - ClassMeta cm = session.getClassMeta(typeName, pMeta, eType); - if (cm != null) - sType = eType = cm; - - if (sType.isObject()) - o = parseIntoCollection(session, r, new ObjectList(session), sType, pMeta); - else if (sType.isCollection() || sType.isObject()) - o = parseIntoCollection(session, r, (Collection)(sType.canCreateNewInstance(outer) - ? sType.newInstance(outer) : new ObjectList(session)), sType, pMeta); - else if (sType.isArray() || sType.isArgs()) - o = session.toArray(sType, parseIntoCollection(session, r, new ArrayList(), sType, pMeta)); - else - isValid = false; - skipTag(r, xUL); - - } - - if (! isValid) - throw new XmlParseException(r.getLocation(), "Unexpected tag ''{0}'' for type ''{1}''", tag, eType); - - if (transform != null && o != null) - o = transform.unswap(session, o, eType); - - if (outer != null) - setParent(eType, o, outer); - - skipWs(r); - return (T)o; - } - - /** - * For parsing output from HtmlDocSerializer, this skips over the head, title, and links. - */ - private static HtmlTag skipToData(XMLStreamReader r) throws Exception { - while (true) { - int event = r.next(); - if (event == START_ELEMENT && "div".equals(r.getLocalName()) && "data".equals(r.getAttributeValue(null, "id"))) { - r.nextTag(); - event = r.getEventType(); - boolean isEmpty = (event == END_ELEMENT); - // Skip until we find a start element, end document, or non-empty text. - if (! isEmpty) - event = skipWs(r); - if (event == END_DOCUMENT) - throw new XmlParseException(r.getLocation(), "Unexpected end of stream looking for data."); - return (event == CHARACTERS ? null : HtmlTag.forString(r.getName().getLocalPart(), false)); - } - } - } - - private static String getAttribute(XMLStreamReader r, String name, String def) { - for (int i = 0; i < r.getAttributeCount(); i++) - if (r.getAttributeLocalName(i).equals(name)) - return r.getAttributeValue(i); - return def; - } - - /* - * Reads an anchor tag and converts it into a bean. - */ - private static <T> T parseAnchor(HtmlParserSession session, XMLStreamReader r, ClassMeta<T> beanType) - throws Exception { - String href = r.getAttributeValue(null, "href"); - String name = session.getElementText(r); - Class<T> beanClass = beanType.getInnerClass(); - if (beanClass.isAnnotationPresent(HtmlLink.class)) { - HtmlLink h = beanClass.getAnnotation(HtmlLink.class); - BeanMap<T> m = session.newBeanMap(beanClass); - m.put(h.hrefProperty(), href); - m.put(h.nameProperty(), name); - return m.getBean(); - } - return session.convertToType(href, beanType); - } - - private static Map<String,String> getAttributes(XMLStreamReader r) { - Map<String,String> m = new TreeMap<String,String>() ; - for (int i = 0; i < r.getAttributeCount(); i++) - m.put(r.getAttributeLocalName(i), r.getAttributeValue(i)); - return m; - } - - /* - * Reads contents of <table> element. - * Precondition: Must be pointing at <table> event. - * Postcondition: Pointing at next START_ELEMENT or END_DOCUMENT event. - */ - private <K,V> Map<K,V> parseIntoMap(HtmlParserSession session, XMLStreamReader r, Map<K,V> m, ClassMeta<K> keyType, - ClassMeta<V> valueType, BeanPropertyMeta pMeta) throws Exception { - while (true) { - HtmlTag tag = nextTag(r, TR, xTABLE); - if (tag == xTABLE) - break; - tag = nextTag(r, TD, TH); - // Skip over the column headers. - if (tag == TH) { - skipTag(r); - r.nextTag(); - skipTag(r); - } else { - K key = parseAnything(session, keyType, r, m, false, pMeta); - nextTag(r, TD); - V value = parseAnything(session, valueType, r, m, false, pMeta); - setName(valueType, value, key); - m.put(key, value); - } - nextTag(r, xTR); - } - - return m; - } - - /* - * Reads contents of <ul> element. - * Precondition: Must be pointing at event following <ul> event. - * Postcondition: Pointing at next START_ELEMENT or END_DOCUMENT event. - */ - private <E> Collection<E> parseIntoCollection(HtmlParserSession session, XMLStreamReader r, Collection<E> l, - ClassMeta<?> type, BeanPropertyMeta pMeta) throws Exception { - int argIndex = 0; - while (true) { - HtmlTag tag = nextTag(r, LI, xUL); - if (tag == xUL) - break; - ClassMeta<?> elementType = type.isArgs() ? type.getArg(argIndex++) : type.getElementType(); - l.add((E)parseAnything(session, elementType, r, l, false, pMeta)); - } - return l; - } - - /* - * Reads contents of <ul> element. - * Precondition: Must be pointing at event following <ul> event. - * Postcondition: Pointing at next START_ELEMENT or END_DOCUMENT event. - */ - private <E> Collection<E> parseTableIntoCollection(HtmlParserSession session, XMLStreamReader r, Collection<E> l, - ClassMeta<E> type, BeanPropertyMeta pMeta) throws Exception { - - HtmlTag tag = nextTag(r, TR); - List<String> keys = new ArrayList<String>(); - while (true) { - tag = nextTag(r, TH, xTR); - if (tag == xTR) - break; - keys.add(session.getElementText(r)); - } - - int argIndex = 0; - - while (true) { - r.nextTag(); - tag = HtmlTag.forEvent(r); - if (tag == xTABLE) - break; - - ClassMeta elementType = null; - String beanType = getAttribute(r, session.getBeanTypePropertyName(type), null); - if (beanType != null) - elementType = session.getClassMeta(beanType, pMeta, null); - if (elementType == null) - elementType = type.isArgs() ? type.getArg(argIndex++) : type.getElementType(); - if (elementType == null) - elementType = session.object(); - - if (elementType.canCreateNewBean(l)) { - BeanMap m = session.newBeanMap(l, elementType.getInnerClass()); - for (int i = 0; i < keys.size(); i++) { - tag = nextTag(r, TD, NULL); - if (tag == NULL) { - m = null; - nextTag(r, xNULL); - break; - } - String key = keys.get(i); - BeanMapEntry e = m.getProperty(key); - if (e == null) { - //onUnknownProperty(key, m, -1, -1); - parseAnything(session, object(), r, l, false, null); - } else { - BeanPropertyMeta bpm = e.getMeta(); - ClassMeta<?> cm = bpm.getClassMeta(); - Object value = parseAnything(session, cm, r, m.getBean(false), false, bpm); - setName(cm, value, key); - bpm.set(m, key, value); - } - } - l.add(m == null ? null : (E)m.getBean()); - } else { - String c = getAttributes(r).get(session.getBeanTypePropertyName(type.getElementType())); - Map m = (Map)(elementType.isMap() && elementType.canCreateNewInstance(l) ? elementType.newInstance(l) - : new ObjectMap(session)); - for (int i = 0; i < keys.size(); i++) { - tag = nextTag(r, TD, NULL); - if (tag == NULL) { - m = null; - nextTag(r, xNULL); - break; - } - String key = keys.get(i); - if (m != null) { - ClassMeta<?> kt = elementType.getKeyType(), vt = elementType.getValueType(); - Object value = parseAnything(session, vt, r, l, false, pMeta); - setName(vt, value, key); - m.put(session.convertToType(key, kt), value); - } - } - if (m != null && c != null) { - ObjectMap m2 = (m instanceof ObjectMap ? (ObjectMap)m : new ObjectMap(m).setBeanSession(session)); - m2.put(session.getBeanTypePropertyName(type.getElementType()), c); - l.add((E)session.cast(m2, pMeta, elementType)); - } else { - l.add((E)m); - } - } - nextTag(r, xTR); - } - return l; - } - - /* - * Reads contents of <table> element. - * Precondition: Must be pointing at event following <table> event. - * Postcondition: Pointing at next START_ELEMENT or END_DOCUMENT event. - */ - private <T> BeanMap<T> parseIntoBean(HtmlParserSession session, XMLStreamReader r, BeanMap<T> m) throws Exception { - while (true) { - HtmlTag tag = nextTag(r, TR, xTABLE); - if (tag == xTABLE) - break; - tag = nextTag(r, TD, TH); - // Skip over the column headers. - if (tag == TH) { - skipTag(r); - r.nextTag(); - skipTag(r); - } else { - String key = session.getElementText(r); - nextTag(r, TD); - BeanPropertyMeta pMeta = m.getPropertyMeta(key); - if (pMeta == null) { - session.onUnknownProperty(key, m, -1, -1); - parseAnything(session, object(), r, null, false, null); - } else { - ClassMeta<?> cm = pMeta.getClassMeta(); - Object value = parseAnything(session, cm, r, m.getBean(false), false, pMeta); - setName(cm, value, key); - pMeta.set(m, key, value); - } - } - nextTag(r, xTR); - } - return m; - } - - /* - * Reads the next tag. Advances past anything that's not a start or end tag. Throws an exception if - * it's not one of the expected tags. - * Precondition: Must be pointing before the event we want to parse. - * Postcondition: Pointing at the tag just parsed. - */ - private static HtmlTag nextTag(XMLStreamReader r, HtmlTag...expected) throws Exception { - int et = r.next(); - - while (et != START_ELEMENT && et != END_ELEMENT && et != END_DOCUMENT) - et = r.next(); - - if (et == END_DOCUMENT) - throw new XmlParseException(r.getLocation(), "Unexpected end of document."); - - HtmlTag tag = HtmlTag.forEvent(r); - if (expected.length == 0) - return tag; - for (HtmlTag t : expected) - if (t == tag) - return tag; - - throw new XmlParseException(r.getLocation(), "Unexpected tag: ''{0}''. Expected one of the following: {1}", tag, expected); - } - - /* - * Skips over the current element and advances to the next element. - * <p> - * Precondition: Pointing to opening tag. - * Postcondition: Pointing to next opening tag. - * - * @param r The stream being read from. - * @throws XMLStreamException - */ - private static void skipTag(XMLStreamReader r) throws Exception { - int et = r.getEventType(); - - if (et != START_ELEMENT) - throw new XmlParseException( - r.getLocation(), - "skipToNextTag() call on invalid event ''{0}''. Must only be called on START_ELEMENT events.", - XmlUtils.toReadableEvent(r) - ); - - String n = r.getLocalName(); - - int depth = 0; - while (true) { - et = r.next(); - if (et == START_ELEMENT) { - String n2 = r.getLocalName(); - if (n.equals(n2)) - depth++; - } else if (et == END_ELEMENT) { - String n2 = r.getLocalName(); - if (n.equals(n2)) - depth--; - if (depth < 0) - return; - } - } - } - - private static void skipTag(XMLStreamReader r, HtmlTag...expected) throws Exception { - HtmlTag tag = HtmlTag.forEvent(r); - if (tag.isOneOf(expected)) - r.next(); - else - throw new XmlParseException( - r.getLocation(), - "Unexpected tag: ''{0}''. Expected one of the following: {1}", - tag, expected); - } - - private static int skipWs(XMLStreamReader r) throws XMLStreamException { - int event = r.getEventType(); - while (event != START_ELEMENT && event != END_ELEMENT && event != END_DOCUMENT && r.isWhiteSpace()) - event = r.next(); - return event; - } - - - //-------------------------------------------------------------------------------- - // Entry point methods - //-------------------------------------------------------------------------------- - @Override /* Parser */ - public HtmlParserSession createSession(Object input, ObjectMap op, Method javaMethod, Object outer, Locale locale, - TimeZone timeZone, MediaType mediaType) { - return new HtmlParserSession(ctx, op, input, javaMethod, outer, locale, timeZone, mediaType); - } - - @Override /* Parser */ - protected <T> T doParse(ParserSession session, ClassMeta<T> type) throws Exception { - HtmlParserSession s = (HtmlParserSession)session; - return parseAnything(s, type, s.getXmlStreamReader(), s.getOuter(), true, null); - } - - @Override /* ReaderParser */ - protected <K,V> Map<K,V> doParseIntoMap(ParserSession session, Map<K,V> m, Type keyType, Type valueType) - throws Exception { - HtmlParserSession s = (HtmlParserSession)session; - return parseIntoMap(s, s.getXmlStreamReader(), m, (ClassMeta<K>)s.getClassMeta(keyType), - (ClassMeta<V>)s.getClassMeta(valueType), null); - } - - @Override /* ReaderParser */ - protected <E> Collection<E> doParseIntoCollection(ParserSession session, Collection<E> c, Type elementType) - throws Exception { - HtmlParserSession s = (HtmlParserSession)session; - return parseIntoCollection(s, s.getXmlStreamReader(), c, s.getClassMeta(elementType), null); + public HtmlParserSession createSession(ParserSessionArgs args) { + return new HtmlParserSession(ctx, args); } }
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserSession.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserSession.java index 5b4d9d6..376843a 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserSession.java @@ -16,22 +16,24 @@ import static javax.xml.stream.XMLStreamConstants.*; import static org.apache.juneau.html.HtmlTag.*; import static org.apache.juneau.internal.StringUtils.*; -import java.io.*; import java.lang.reflect.*; import java.util.*; import javax.xml.stream.*; import org.apache.juneau.*; -import org.apache.juneau.http.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.transform.*; import org.apache.juneau.xml.*; /** * Session object that lives for the duration of a single use of {@link HtmlParser}. * * <p> - * This class is NOT thread safe. It is meant to be discarded after one-time use. + * This class is NOT thread safe. + * It is typically discarded after one-time use although it can be reused against multiple inputs. */ +@SuppressWarnings({ "unchecked", "rawtypes" }) public final class HtmlParserSession extends XmlParserSession { private static final Set<String> whitespaceElements = new HashSet<String>( @@ -46,31 +48,527 @@ public final class HtmlParserSession extends XmlParserSession { * @param ctx * The context creating this session object. * The context contains all the configuration settings for this object. - * @param input - * The input. Can be any of the following types: - * <ul> - * <li><jk>null</jk> - * <li>{@link Reader} - * <li>{@link CharSequence} - * <li>{@link InputStream} containing UTF-8 encoded text. - * <li>{@link File} containing system encoded text. - * </ul> - * @param op - * The override properties. - * These override any context properties defined in the context. - * @param javaMethod The java method that called this parser, usually the method in a REST servlet. - * @param outer The outer object for instantiating top-level non-static inner classes. - * @param locale - * The session locale. - * If <jk>null</jk>, then the locale defined on the context is used. - * @param timeZone - * The session timezone. - * If <jk>null</jk>, then the timezone defined on the context is used. - * @param mediaType The session media type (e.g. <js>"application/json"</js>). + * @param args + * Runtime session arguments. */ - public HtmlParserSession(HtmlParserContext ctx, ObjectMap op, Object input, Method javaMethod, Object outer, - Locale locale, TimeZone timeZone, MediaType mediaType) { - super(ctx, op, input, javaMethod, outer, locale, timeZone, mediaType); + protected HtmlParserSession(HtmlParserContext ctx, ParserSessionArgs args) { + super(ctx, args); + } + + @Override /* ParserSession */ + protected <T> T doParse(ParserPipe pipe, ClassMeta<T> type) throws Exception { + return parseAnything(type, getXmlReader(pipe), getOuter(), true, null); + } + + @Override /* ReaderParserSession */ + protected <K,V> Map<K,V> doParseIntoMap(ParserPipe pipe, Map<K,V> m, Type keyType, Type valueType) + throws Exception { + return parseIntoMap(getXmlReader(pipe), m, (ClassMeta<K>)getClassMeta(keyType), + (ClassMeta<V>)getClassMeta(valueType), null); + } + + @Override /* ReaderParserSession */ + protected <E> Collection<E> doParseIntoCollection(ParserPipe pipe, Collection<E> c, Type elementType) + throws Exception { + return parseIntoCollection(getXmlReader(pipe), c, getClassMeta(elementType), null); + } + + /* + * Reads anything starting at the current event. + * <p> + * Precondition: Must be pointing at outer START_ELEMENT. + * Postcondition: Pointing at outer END_ELEMENT. + */ + private <T> T parseAnything(ClassMeta<T> eType, XmlReader r, Object outer, boolean isRoot, BeanPropertyMeta pMeta) throws Exception { + + if (eType == null) + eType = (ClassMeta<T>)object(); + PojoSwap<T,Object> transform = (PojoSwap<T,Object>)eType.getPojoSwap(); + ClassMeta<?> sType = eType.getSerializedClassMeta(); + setCurrentClass(sType); + + int event = r.getEventType(); + if (event != START_ELEMENT) + throw new XmlParseException(r.getLocation(), "parseAnything must be called on outer start element."); + + if (! isRoot) + event = r.next(); + boolean isEmpty = (event == END_ELEMENT); + + // Skip until we find a start element, end document, or non-empty text. + if (! isEmpty) + event = skipWs(r); + + if (event == END_DOCUMENT) + throw new XmlParseException(r.getLocation(), "Unexpected end of stream in parseAnything for type ''{0}''", eType); + + // Handle @Html(asXml=true) beans. + HtmlClassMeta hcm = sType.getExtendedMeta(HtmlClassMeta.class); + if (hcm.isAsXml()) + return super.parseAnything(eType, null, r, outer, false, pMeta); + + Object o = null; + + boolean isValid = true; + HtmlTag tag = (event == CHARACTERS ? null : HtmlTag.forString(r.getName().getLocalPart(), false)); + + // If it's not a known tag, then parse it as XML. + // Allows us to parse stuff like "<div/>" into HTML5 beans. + if (tag == null && event != CHARACTERS) + return super.parseAnything(eType, null, r, outer, false, pMeta); + + if (tag == HTML) + tag = skipToData(r); + + if (isEmpty) { + o = ""; + } else if (tag == null || tag.isOneOf(BR,BS,FF,SP)) { + String text = parseText(r); + if (sType.isObject() || sType.isCharSequence()) + o = text; + else if (sType.isChar()) + o = text.charAt(0); + else if (sType.isBoolean()) + o = Boolean.parseBoolean(text); + else if (sType.isNumber()) + o = parseNumber(text, (Class<? extends Number>)eType.getInnerClass()); + else if (sType.canCreateNewInstanceFromString(outer)) + o = sType.newInstanceFromString(outer, text); + else if (sType.canCreateNewInstanceFromNumber(outer)) + o = sType.newInstanceFromNumber(this, outer, parseNumber(text, sType.getNewInstanceFromNumberClass())); + else + isValid = false; + + } else if (tag == STRING || (tag == A && pMeta != null + && pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).getLink() != null)) { + String text = getElementText(r); + if (sType.isObject() || sType.isCharSequence()) + o = text; + else if (sType.isChar()) + o = text.charAt(0); + else if (sType.canCreateNewInstanceFromString(outer)) + o = sType.newInstanceFromString(outer, text); + else if (sType.canCreateNewInstanceFromNumber(outer)) + o = sType.newInstanceFromNumber(this, outer, parseNumber(text, sType.getNewInstanceFromNumberClass())); + else + isValid = false; + skipTag(r, tag == STRING ? xSTRING : xA); + + } else if (tag == NUMBER) { + String text = getElementText(r); + if (sType.isObject()) + o = parseNumber(text, Number.class); + else if (sType.isNumber()) + o = parseNumber(text, (Class<? extends Number>)sType.getInnerClass()); + else if (sType.canCreateNewInstanceFromNumber(outer)) + o = sType.newInstanceFromNumber(this, outer, parseNumber(text, sType.getNewInstanceFromNumberClass())); + else + isValid = false; + skipTag(r, xNUMBER); + + } else if (tag == BOOLEAN) { + String text = getElementText(r); + if (sType.isObject() || sType.isBoolean()) + o = Boolean.parseBoolean(text); + else + isValid = false; + skipTag(r, xBOOLEAN); + + } else if (tag == P) { + String text = getElementText(r); + if (! "No Results".equals(text)) + isValid = false; + skipTag(r, xP); + + } else if (tag == NULL) { + skipTag(r, NULL); + skipTag(r, xNULL); + + } else if (tag == A) { + o = parseAnchor(r, eType); + skipTag(r, xA); + + } else if (tag == TABLE) { + + String typeName = getAttribute(r, getBeanTypePropertyName(eType), "object"); + ClassMeta cm = getClassMeta(typeName, pMeta, eType); + + if (cm != null) { + sType = eType = cm; + typeName = sType.isArray() ? "array" : "object"; + } else if (! "array".equals(typeName)) { + // Type name could be a subtype name. + typeName = "object"; + } + + if (typeName.equals("object")) { + if (sType.isObject()) { + o = parseIntoMap(r, (Map)new ObjectMap(this), sType.getKeyType(), sType.getValueType(), + pMeta); + } else if (sType.isMap()) { + o = parseIntoMap(r, (Map)(sType.canCreateNewInstance(outer) ? sType.newInstance(outer) + : new ObjectMap(this)), sType.getKeyType(), sType.getValueType(), pMeta); + } else if (sType.canCreateNewBean(outer)) { + BeanMap m = newBeanMap(outer, sType.getInnerClass()); + o = parseIntoBean(r, m).getBean(); + } else { + isValid = false; + } + skipTag(r, xTABLE); + + } else if (typeName.equals("array")) { + if (sType.isObject()) + o = parseTableIntoCollection(r, (Collection)new ObjectList(this), sType, pMeta); + else if (sType.isCollection()) + o = parseTableIntoCollection(r, (Collection)(sType.canCreateNewInstance(outer) + ? sType.newInstance(outer) : new ObjectList(this)), sType, pMeta); + else if (sType.isArray() || sType.isArgs()) { + ArrayList l = (ArrayList)parseTableIntoCollection(r, new ArrayList(), sType, pMeta); + o = toArray(sType, l); + } + else + isValid = false; + skipTag(r, xTABLE); + + } else { + isValid = false; + } + + } else if (tag == UL) { + String typeName = getAttribute(r, getBeanTypePropertyName(eType), "array"); + ClassMeta cm = getClassMeta(typeName, pMeta, eType); + if (cm != null) + sType = eType = cm; + + if (sType.isObject()) + o = parseIntoCollection(r, new ObjectList(this), sType, pMeta); + else if (sType.isCollection() || sType.isObject()) + o = parseIntoCollection(r, (Collection)(sType.canCreateNewInstance(outer) + ? sType.newInstance(outer) : new ObjectList(this)), sType, pMeta); + else if (sType.isArray() || sType.isArgs()) + o = toArray(sType, parseIntoCollection(r, new ArrayList(), sType, pMeta)); + else + isValid = false; + skipTag(r, xUL); + + } + + if (! isValid) + throw new XmlParseException(r.getLocation(), "Unexpected tag ''{0}'' for type ''{1}''", tag, eType); + + if (transform != null && o != null) + o = transform.unswap(this, o, eType); + + if (outer != null) + setParent(eType, o, outer); + + skipWs(r); + return (T)o; + } + + /* + * For parsing output from HtmlDocSerializer, this skips over the head, title, and links. + */ + private static HtmlTag skipToData(XmlReader r) throws Exception { + while (true) { + int event = r.next(); + if (event == START_ELEMENT && "div".equals(r.getLocalName()) && "data".equals(r.getAttributeValue(null, "id"))) { + r.nextTag(); + event = r.getEventType(); + boolean isEmpty = (event == END_ELEMENT); + // Skip until we find a start element, end document, or non-empty text. + if (! isEmpty) + event = skipWs(r); + if (event == END_DOCUMENT) + throw new XmlParseException(r.getLocation(), "Unexpected end of stream looking for data."); + return (event == CHARACTERS ? null : HtmlTag.forString(r.getName().getLocalPart(), false)); + } + } + } + + private static String getAttribute(XmlReader r, String name, String def) { + for (int i = 0; i < r.getAttributeCount(); i++) + if (r.getAttributeLocalName(i).equals(name)) + return r.getAttributeValue(i); + return def; + } + + /* + * Reads an anchor tag and converts it into a bean. + */ + private <T> T parseAnchor(XmlReader r, ClassMeta<T> beanType) + throws Exception { + String href = r.getAttributeValue(null, "href"); + String name = getElementText(r); + Class<T> beanClass = beanType.getInnerClass(); + if (beanClass.isAnnotationPresent(HtmlLink.class)) { + HtmlLink h = beanClass.getAnnotation(HtmlLink.class); + BeanMap<T> m = newBeanMap(beanClass); + m.put(h.hrefProperty(), href); + m.put(h.nameProperty(), name); + return m.getBean(); + } + return convertToType(href, beanType); + } + + private static Map<String,String> getAttributes(XmlReader r) { + Map<String,String> m = new TreeMap<String,String>() ; + for (int i = 0; i < r.getAttributeCount(); i++) + m.put(r.getAttributeLocalName(i), r.getAttributeValue(i)); + return m; + } + + /* + * Reads contents of <table> element. + * Precondition: Must be pointing at <table> event. + * Postcondition: Pointing at next START_ELEMENT or END_DOCUMENT event. + */ + private <K,V> Map<K,V> parseIntoMap(XmlReader r, Map<K,V> m, ClassMeta<K> keyType, + ClassMeta<V> valueType, BeanPropertyMeta pMeta) throws Exception { + while (true) { + HtmlTag tag = nextTag(r, TR, xTABLE); + if (tag == xTABLE) + break; + tag = nextTag(r, TD, TH); + // Skip over the column headers. + if (tag == TH) { + skipTag(r); + r.nextTag(); + skipTag(r); + } else { + K key = parseAnything(keyType, r, m, false, pMeta); + nextTag(r, TD); + V value = parseAnything(valueType, r, m, false, pMeta); + setName(valueType, value, key); + m.put(key, value); + } + nextTag(r, xTR); + } + + return m; + } + + /* + * Reads contents of <ul> element. + * Precondition: Must be pointing at event following <ul> event. + * Postcondition: Pointing at next START_ELEMENT or END_DOCUMENT event. + */ + private <E> Collection<E> parseIntoCollection(XmlReader r, Collection<E> l, + ClassMeta<?> type, BeanPropertyMeta pMeta) throws Exception { + int argIndex = 0; + while (true) { + HtmlTag tag = nextTag(r, LI, xUL); + if (tag == xUL) + break; + ClassMeta<?> elementType = type.isArgs() ? type.getArg(argIndex++) : type.getElementType(); + l.add((E)parseAnything(elementType, r, l, false, pMeta)); + } + return l; + } + + /* + * Reads contents of <ul> element. + * Precondition: Must be pointing at event following <ul> event. + * Postcondition: Pointing at next START_ELEMENT or END_DOCUMENT event. + */ + private <E> Collection<E> parseTableIntoCollection(XmlReader r, Collection<E> l, + ClassMeta<E> type, BeanPropertyMeta pMeta) throws Exception { + + HtmlTag tag = nextTag(r, TR); + List<String> keys = new ArrayList<String>(); + while (true) { + tag = nextTag(r, TH, xTR); + if (tag == xTR) + break; + keys.add(getElementText(r)); + } + + int argIndex = 0; + + while (true) { + r.nextTag(); + tag = HtmlTag.forEvent(r); + if (tag == xTABLE) + break; + + ClassMeta elementType = null; + String beanType = getAttribute(r, getBeanTypePropertyName(type), null); + if (beanType != null) + elementType = getClassMeta(beanType, pMeta, null); + if (elementType == null) + elementType = type.isArgs() ? type.getArg(argIndex++) : type.getElementType(); + if (elementType == null) + elementType = object(); + + if (elementType.canCreateNewBean(l)) { + BeanMap m = newBeanMap(l, elementType.getInnerClass()); + for (int i = 0; i < keys.size(); i++) { + tag = nextTag(r, TD, NULL); + if (tag == NULL) { + m = null; + nextTag(r, xNULL); + break; + } + String key = keys.get(i); + BeanMapEntry e = m.getProperty(key); + if (e == null) { + //onUnknownProperty(key, m, -1, -1); + parseAnything(object(), r, l, false, null); + } else { + BeanPropertyMeta bpm = e.getMeta(); + ClassMeta<?> cm = bpm.getClassMeta(); + Object value = parseAnything(cm, r, m.getBean(false), false, bpm); + setName(cm, value, key); + bpm.set(m, key, value); + } + } + l.add(m == null ? null : (E)m.getBean()); + } else { + String c = getAttributes(r).get(getBeanTypePropertyName(type.getElementType())); + Map m = (Map)(elementType.isMap() && elementType.canCreateNewInstance(l) ? elementType.newInstance(l) + : new ObjectMap(this)); + for (int i = 0; i < keys.size(); i++) { + tag = nextTag(r, TD, NULL); + if (tag == NULL) { + m = null; + nextTag(r, xNULL); + break; + } + String key = keys.get(i); + if (m != null) { + ClassMeta<?> kt = elementType.getKeyType(), vt = elementType.getValueType(); + Object value = parseAnything(vt, r, l, false, pMeta); + setName(vt, value, key); + m.put(convertToType(key, kt), value); + } + } + if (m != null && c != null) { + ObjectMap m2 = (m instanceof ObjectMap ? (ObjectMap)m : new ObjectMap(m).setBeanSession(this)); + m2.put(getBeanTypePropertyName(type.getElementType()), c); + l.add((E)cast(m2, pMeta, elementType)); + } else { + l.add((E)m); + } + } + nextTag(r, xTR); + } + return l; + } + + /* + * Reads contents of <table> element. + * Precondition: Must be pointing at event following <table> event. + * Postcondition: Pointing at next START_ELEMENT or END_DOCUMENT event. + */ + private <T> BeanMap<T> parseIntoBean(XmlReader r, BeanMap<T> m) throws Exception { + while (true) { + HtmlTag tag = nextTag(r, TR, xTABLE); + if (tag == xTABLE) + break; + tag = nextTag(r, TD, TH); + // Skip over the column headers. + if (tag == TH) { + skipTag(r); + r.nextTag(); + skipTag(r); + } else { + String key = getElementText(r); + nextTag(r, TD); + BeanPropertyMeta pMeta = m.getPropertyMeta(key); + if (pMeta == null) { + onUnknownProperty(r.getPipe(), key, m, -1, -1); + parseAnything(object(), r, null, false, null); + } else { + ClassMeta<?> cm = pMeta.getClassMeta(); + Object value = parseAnything(cm, r, m.getBean(false), false, pMeta); + setName(cm, value, key); + pMeta.set(m, key, value); + } + } + nextTag(r, xTR); + } + return m; + } + + /* + * Reads the next tag. Advances past anything that's not a start or end tag. Throws an exception if + * it's not one of the expected tags. + * Precondition: Must be pointing before the event we want to parse. + * Postcondition: Pointing at the tag just parsed. + */ + private static HtmlTag nextTag(XmlReader r, HtmlTag...expected) throws Exception { + int et = r.next(); + + while (et != START_ELEMENT && et != END_ELEMENT && et != END_DOCUMENT) + et = r.next(); + + if (et == END_DOCUMENT) + throw new XmlParseException(r.getLocation(), "Unexpected end of document."); + + HtmlTag tag = HtmlTag.forEvent(r); + if (expected.length == 0) + return tag; + for (HtmlTag t : expected) + if (t == tag) + return tag; + + throw new XmlParseException(r.getLocation(), "Unexpected tag: ''{0}''. Expected one of the following: {1}", tag, expected); + } + + /* + * Skips over the current element and advances to the next element. + * <p> + * Precondition: Pointing to opening tag. + * Postcondition: Pointing to next opening tag. + * + * @param r The stream being read from. + * @throws XMLStreamException + */ + private static void skipTag(XmlReader r) throws Exception { + int et = r.getEventType(); + + if (et != START_ELEMENT) + throw new XmlParseException( + r.getLocation(), + "skipToNextTag() call on invalid event ''{0}''. Must only be called on START_ELEMENT events.", + XmlUtils.toReadableEvent(r) + ); + + String n = r.getLocalName(); + + int depth = 0; + while (true) { + et = r.next(); + if (et == START_ELEMENT) { + String n2 = r.getLocalName(); + if (n.equals(n2)) + depth++; + } else if (et == END_ELEMENT) { + String n2 = r.getLocalName(); + if (n.equals(n2)) + depth--; + if (depth < 0) + return; + } + } + } + + private static void skipTag(XmlReader r, HtmlTag...expected) throws Exception { + HtmlTag tag = HtmlTag.forEvent(r); + if (tag.isOneOf(expected)) + r.next(); + else + throw new XmlParseException( + r.getLocation(), + "Unexpected tag: ''{0}''. Expected one of the following: {1}", + tag, expected); + } + + private static int skipWs(XmlReader r) throws XMLStreamException { + int event = r.getEventType(); + while (event != START_ELEMENT && event != END_ELEMENT && event != END_DOCUMENT && r.isWhiteSpace()) + event = r.next(); + return event; } /** @@ -85,7 +583,7 @@ public final class HtmlParserSession extends XmlParserSession { * @throws XMLStreamException */ @Override /* XmlParserSession */ - public String parseText(XMLStreamReader r) throws Exception { + protected final String parseText(XmlReader r) throws Exception { StringBuilder sb = getStringBuilder(); @@ -165,7 +663,7 @@ public final class HtmlParserSession extends XmlParserSession { } /** - * Identical to {@link #parseText(XMLStreamReader)} except assumes the current event is the opening tag. + * Identical to {@link #parseText(XmlReader)} except assumes the current event is the opening tag. * * <p> * Precondition: Pointing to opening tag. @@ -176,19 +674,19 @@ public final class HtmlParserSession extends XmlParserSession { * @throws XMLStreamException */ @Override /* XmlParserSession */ - public String getElementText(XMLStreamReader r) throws Exception { + protected final String getElementText(XmlReader r) throws Exception { r.next(); return parseText(r); } @Override /* XmlParserSession */ - public boolean isWhitespaceElement(XMLStreamReader r) { + protected final boolean isWhitespaceElement(XmlReader r) { String s = r.getLocalName(); return whitespaceElements.contains(s); } @Override /* XmlParserSession */ - public String parseWhitespaceElement(XMLStreamReader r) throws Exception { + protected final String parseWhitespaceElement(XmlReader r) throws Exception { HtmlTag tag = HtmlTag.forEvent(r); int et = r.next(); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java index 5d95e3f..ca452f6 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java @@ -12,17 +12,11 @@ // *************************************************************************************************************************** package org.apache.juneau.html; -import static org.apache.juneau.internal.ClassUtils.*; import static org.apache.juneau.serializer.SerializerContext.*; -import java.lang.reflect.*; -import java.util.*; - import org.apache.juneau.*; import org.apache.juneau.annotation.*; -import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; -import org.apache.juneau.transform.*; /** * Serializes POJO metamodels to HTML. @@ -57,108 +51,12 @@ public final class HtmlSchemaDocSerializer extends HtmlDocSerializer { * @param propertyStore The property store to use for creating the context for this serializer. */ public HtmlSchemaDocSerializer(PropertyStore propertyStore) { - super(propertyStore); + super(propertyStore.copy().append(SERIALIZER_detectRecursions, true).append(SERIALIZER_ignoreRecursions, true)); this.ctx = createContext(HtmlDocSerializerContext.class); } - /** - * Constructor. - * - * @param propertyStore The property store to use for creating the context for this serializer. - * @param overrideProperties - */ - public HtmlSchemaDocSerializer(PropertyStore propertyStore, Map<String,Object> overrideProperties) { - super(propertyStore); - this.ctx = this.propertyStore.create(overrideProperties).getContext(HtmlDocSerializerContext.class); - } - - @Override /* CoreObject */ - protected ObjectMap getOverrideProperties() { - return super.getOverrideProperties().append(SERIALIZER_detectRecursions, true).append(SERIALIZER_ignoreRecursions, true); - } - @Override /* Serializer */ - public HtmlDocSerializerSession createSession(ObjectMap op, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) { - return new HtmlDocSerializerSession(ctx, op, javaMethod, locale, timeZone, mediaType, uriContext); - } - - @Override /* ISchemaSerializer */ - protected void doSerialize(SerializerSession session, SerializerOutput out, Object o) throws Exception { - HtmlSerializerSession s = (HtmlSerializerSession)session; - ObjectMap schema = getSchema(s, session.getClassMetaForObject(o), "root", null); - super.doSerialize(s, out, schema); - } - - /* - * Creates a schema representation of the specified class type. - * - * @param eType The class type to get the schema of. - * @param ctx Serialize context used to prevent infinite loops. - * @param attrName The name of the current attribute. - * @return A schema representation of the specified class. - * @throws SerializeException If a problem occurred trying to convert the output. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - private ObjectMap getSchema(HtmlSerializerSession session, ClassMeta<?> eType, String attrName, String[] pNames) throws Exception { - - ObjectMap out = new ObjectMap(); - - ClassMeta<?> aType; // The actual type (will be null if recursion occurs) - ClassMeta<?> sType; // The serialized type - - aType = session.push(attrName, eType, null); - - sType = eType.getSerializedClassMeta(); - String type = null; - - if (sType.isEnum() || sType.isCharSequence() || sType.isChar()) - type = "string"; - else if (sType.isNumber()) - type = "number"; - else if (sType.isBoolean()) - type = "boolean"; - else if (sType.isMapOrBean()) - type = "object"; - else if (sType.isCollectionOrArray()) - type = "array"; - else - type = "any"; - - out.put("type", type); - out.put("class", eType.toString()); - PojoSwap t = eType.getPojoSwap(); - if (t != null) - out.put("transform", t); - - if (aType != null) { - if (sType.isEnum()) - out.put("enum", getEnumStrings((Class<Enum<?>>)sType.getInnerClass())); - else if (sType.isCollectionOrArray()) { - ClassMeta componentType = sType.getElementType(); - if (sType.isCollection() && isParentClass(Set.class, sType.getInnerClass())) - out.put("uniqueItems", true); - out.put("items", getSchema(session, componentType, "items", pNames)); - } else if (sType.isBean()) { - ObjectMap properties = new ObjectMap(); - BeanMeta bm = session.getBeanMeta(sType.getInnerClass()); - if (pNames != null) - bm = new BeanMetaFiltered(bm, pNames); - for (Iterator<BeanPropertyMeta> i = bm.getPropertyMetas().iterator(); i.hasNext();) { - BeanPropertyMeta p = i.next(); - properties.put(p.getName(), getSchema(session, p.getClassMeta(), p.getName(), p.getProperties())); - } - out.put("properties", properties); - } - } - session.pop(); - return out; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static List<String> getEnumStrings(Class<? extends Enum> c) { - List<String> l = new LinkedList<String>(); - for (Object e : EnumSet.allOf(c)) - l.add(e.toString()); - return l; + public HtmlDocSerializerSession createSession(SerializerSessionArgs args) { + return new HtmlDocSerializerSession(ctx, args); } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializerSession.java new file mode 100644 index 0000000..27f020a --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializerSession.java @@ -0,0 +1,124 @@ +// *************************************************************************************************************************** +// * 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 static org.apache.juneau.internal.ClassUtils.*; + +import java.util.*; +import org.apache.juneau.*; +import org.apache.juneau.serializer.*; +import org.apache.juneau.transform.*; + +/** + * Context object that lives for the duration of a single serialization of {@link HtmlSchemaDocSerializer} and its subclasses. + * + * <p> + * See {@link SerializerContext} for details. + * + * <p> + * This class is NOT thread safe. It is meant to be discarded after one-time use. + */ +public class HtmlSchemaDocSerializerSession extends HtmlDocSerializerSession { + + /** + * Create a new session using properties specified in the context. + * + * @param ctx + * The context creating this session object. + * The context contains all the configuration settings for this object. + * @param args + * Runtime arguments. + */ + protected HtmlSchemaDocSerializerSession(HtmlDocSerializerContext ctx, SerializerSessionArgs args) { + super(ctx, args); + } + + @Override /* SerializerSession */ + protected void doSerialize(SerializerPipe out, Object o) throws Exception { + ObjectMap schema = getSchema(getClassMetaForObject(o), "root", null); + super.doSerialize(out, schema); + } + + /* + * Creates a schema representation of the specified class type. + * + * @param eType The class type to get the schema of. + * @param ctx Serialize context used to prevent infinite loops. + * @param attrName The name of the current attribute. + * @return A schema representation of the specified class. + * @throws SerializeException If a problem occurred trying to convert the output. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + private ObjectMap getSchema(ClassMeta<?> eType, String attrName, String[] pNames) throws Exception { + + ObjectMap out = new ObjectMap(); + + ClassMeta<?> aType; // The actual type (will be null if recursion occurs) + ClassMeta<?> sType; // The serialized type + + aType = push(attrName, eType, null); + + sType = eType.getSerializedClassMeta(); + String type = null; + + if (sType.isEnum() || sType.isCharSequence() || sType.isChar()) + type = "string"; + else if (sType.isNumber()) + type = "number"; + else if (sType.isBoolean()) + type = "boolean"; + else if (sType.isMapOrBean()) + type = "object"; + else if (sType.isCollectionOrArray()) + type = "array"; + else + type = "any"; + + out.put("type", type); + out.put("class", eType.toString()); + PojoSwap t = eType.getPojoSwap(); + if (t != null) + out.put("transform", t); + + if (aType != null) { + if (sType.isEnum()) + out.put("enum", getEnumStrings((Class<Enum<?>>)sType.getInnerClass())); + else if (sType.isCollectionOrArray()) { + ClassMeta componentType = sType.getElementType(); + if (sType.isCollection() && isParentClass(Set.class, sType.getInnerClass())) + out.put("uniqueItems", true); + out.put("items", getSchema(componentType, "items", pNames)); + } else if (sType.isBean()) { + ObjectMap properties = new ObjectMap(); + BeanMeta bm = getBeanMeta(sType.getInnerClass()); + if (pNames != null) + bm = new BeanMetaFiltered(bm, pNames); + for (Iterator<BeanPropertyMeta> i = bm.getPropertyMetas().iterator(); i.hasNext();) { + BeanPropertyMeta p = i.next(); + properties.put(p.getName(), getSchema(p.getClassMeta(), p.getName(), p.getProperties())); + } + out.put("properties", properties); + } + } + pop(); + return out; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static List<String> getEnumStrings(Class<? extends Enum> c) { + List<String> l = new LinkedList<String>(); + for (Object e : EnumSet.allOf(c)) + l.add(e.toString()); + return l; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java index 47ba3e8..aa8b6d0 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java @@ -12,21 +12,14 @@ // *************************************************************************************************************************** package org.apache.juneau.html; -import static org.apache.juneau.html.HtmlSerializer.ContentResult.*; import static org.apache.juneau.serializer.SerializerContext.*; -import java.io.*; -import java.lang.reflect.*; import java.util.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; -import org.apache.juneau.http.*; -import org.apache.juneau.internal.*; import org.apache.juneau.serializer.*; -import org.apache.juneau.transform.*; import org.apache.juneau.xml.*; -import org.apache.juneau.xml.annotation.*; /** * Serializes POJO models to HTML. @@ -163,13 +156,7 @@ public class HtmlSerializer extends XmlSerializer { * @param propertyStore The property store containing all the settings for this object. */ public Sq(PropertyStore propertyStore) { - super(propertyStore); - } - - - @Override /* CoreObject */ - protected ObjectMap getOverrideProperties() { - return super.getOverrideProperties().append(SERIALIZER_quoteChar, '\''); + super(propertyStore.copy().append(SERIALIZER_quoteChar, '\'')); } } @@ -182,12 +169,7 @@ public class HtmlSerializer extends XmlSerializer { * @param propertyStore The property store containing all the settings for this object. */ public SqReadable(PropertyStore propertyStore) { - super(propertyStore); - } - - @Override /* CoreObject */ - protected ObjectMap getOverrideProperties() { - return super.getOverrideProperties().append(SERIALIZER_quoteChar, '\'').append(SERIALIZER_useWhitespace, true); + super(propertyStore.copy().append(SERIALIZER_quoteChar, '\'').append(SERIALIZER_useWhitespace, true)); } } @@ -210,601 +192,15 @@ public class HtmlSerializer extends XmlSerializer { return new HtmlSerializerBuilder(propertyStore); } - /** - * Main serialization routine. - * - * @param session The serialization context object. - * @param o The object being serialized. - * @param w The writer to serialize to. - * @return The same writer passed in. - * @throws IOException If a problem occurred trying to send output to the writer. - */ - private HtmlWriter doSerialize(HtmlSerializerSession session, Object o, HtmlWriter w) throws Exception { - serializeAnything(session, w, o, session.getExpectedRootType(o), null, session.getInitialDepth()-1, null, true); - return w; - } - - /** - * Serialize the specified object to the specified writer. - * - * @param session The context object that lives for the duration of this serialization. - * @param out The writer. - * @param o The object to serialize. - * @param eType The expected type of the object if this is a bean property. - * @param name - * The attribute name of this object if this object was a field in a JSON object (i.e. key of a - * {@link java.util.Map.Entry} or property name of a bean). - * @param indent The current indentation value. - * @param pMeta The bean property being serialized, or <jk>null</jk> if we're not serializing a bean property. - * @param isRoot <jk>true</jk> if this is the root element of the document. - * @return The type of content encountered. Either simple (no whitespace) or normal (elements with whitespace). - * @throws Exception If a problem occurred trying to convert the output. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - protected ContentResult serializeAnything(HtmlSerializerSession session, HtmlWriter out, Object o, - ClassMeta<?> eType, String name, int indent, BeanPropertyMeta pMeta, boolean isRoot) throws Exception { - - ClassMeta<?> aType = null; // The actual type - ClassMeta<?> wType = null; // The wrapped type (delegate) - ClassMeta<?> sType = object(); // The serialized type - - if (eType == null) - eType = object(); - - aType = session.push(name, o, eType); - - // Handle recursion - if (aType == null) { - o = null; - aType = object(); - } - - session.indent += indent; - - ContentResult cr = CR_NORMAL; - - // Determine the type. - if (o == null || (aType.isChar() && ((Character)o).charValue() == 0)) { - out.tag("null"); - cr = ContentResult.CR_SIMPLE; - - } else { - - if (aType.isDelegate()) { - wType = aType; - aType = ((Delegate)o).getClassMeta(); - } - - sType = aType.getSerializedClassMeta(); - String typeName = null; - if (session.isAddBeanTypeProperties() && ! eType.equals(aType)) - typeName = aType.getDictionaryName(); - - // Swap if necessary - PojoSwap swap = aType.getPojoSwap(); - if (swap != null) { - o = swap.swap(session, o); - - // If the getSwapClass() method returns Object, we need to figure out - // the actual type now. - if (sType.isObject()) - sType = session.getClassMetaForObject(o); - } - - HtmlClassMeta html = sType.getExtendedMeta(HtmlClassMeta.class); - HtmlRender render = (pMeta == null ? null : pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).getRender()); - if (render == null) - render = html.getRender(); - - if (render != null) { - Object o2 = render.getContent(session, o); - if (o2 != o) { - session.indent -= indent; - session.pop(); - out.nl(session.indent); - return serializeAnything(session, out, o2, null, typeName, indent, null, false); - } - } - - if (html.isAsXml() || (pMeta != null && pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isAsXml())) { - super.serializeAnything(session, out, o, null, null, null, false, XmlFormat.MIXED, false, false, null); - - } else if (html.isAsPlainText() || (pMeta != null && pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isAsPlainText())) { - out.write(o == null ? "null" : o.toString()); - cr = CR_SIMPLE; - - } else if (o == null || (sType.isChar() && ((Character)o).charValue() == 0)) { - out.tag("null"); - cr = CR_SIMPLE; - - } else if (sType.isNumber()) { - if (eType.isNumber() && ! isRoot) - out.append(o); - else - out.sTag("number").append(o).eTag("number"); - cr = CR_SIMPLE; - - } else if (sType.isBoolean()) { - if (eType.isBoolean() && ! isRoot) - out.append(o); - else - out.sTag("boolean").append(o).eTag("boolean"); - cr = CR_SIMPLE; - - } else if (sType.isMap() || (wType != null && wType.isMap())) { - out.nlIf(! isRoot, indent+1); - if (o instanceof BeanMap) - serializeBeanMap(session, out, (BeanMap)o, eType, pMeta); - else - serializeMap(session, out, (Map)o, sType, eType.getKeyType(), eType.getValueType(), typeName, pMeta); - - } else if (sType.isBean()) { - BeanMap m = session.toBeanMap(o); - Class<?> c = o.getClass(); - if (c.isAnnotationPresent(HtmlLink.class)) { - HtmlLink h = o.getClass().getAnnotation(HtmlLink.class); - Object urlProp = m.get(h.hrefProperty()); - Object nameProp = m.get(h.nameProperty()); - out.oTag("a").attrUri("href", urlProp).append('>').text(nameProp).eTag("a"); - cr = CR_SIMPLE; - } else { - out.nlIf(! isRoot, indent+2); - serializeBeanMap(session, out, m, eType, pMeta); - } - - } else if (sType.isCollection() || sType.isArray() || (wType != null && wType.isCollection())) { - out.nlIf(! isRoot, indent+1); - serializeCollection(session, out, o, sType, eType, name, pMeta); - - } else if (session.isUri(sType, pMeta, o)) { - String label = session.getAnchorText(pMeta, o); - out.oTag("a").attrUri("href", o).append('>'); - out.text(label); - out.eTag("a"); - cr = CR_SIMPLE; - - } else { - if (isRoot) - out.sTag("string").text(session.toString(o)).eTag("string"); - else - out.text(session.toString(o)); - cr = CR_SIMPLE; - } - } - session.pop(); - session.indent -= indent; - return cr; - } - - /** - * Identifies what the contents were of a serialized bean. - */ - static enum ContentResult { - CR_SIMPLE, // Simple content. Shouldn't use whitespace. - CR_NORMAL // Normal content. Use whitespace. - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private void serializeMap(HtmlSerializerSession session, HtmlWriter out, Map m, ClassMeta<?> sType, - ClassMeta<?> eKeyType, ClassMeta<?> eValueType, String typeName, BeanPropertyMeta ppMeta) throws Exception { - - ClassMeta<?> keyType = eKeyType == null ? session.string() : eKeyType; - ClassMeta<?> valueType = eValueType == null ? session.object() : eValueType; - ClassMeta<?> aType = session.getClassMetaForObject(m); // The actual type - - int i = session.getIndent(); - - out.oTag(i, "table"); - - if (typeName != null && ppMeta != null && ppMeta.getClassMeta() != aType) - out.attr(session.getBeanTypePropertyName(sType), typeName); - - out.append(">").nl(i+1); - if (session.isAddKeyValueTableHeaders() && ! (aType.getExtendedMeta(HtmlClassMeta.class).isNoTableHeaders() - || (ppMeta != null && ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isNoTableHeaders()))) { - out.sTag(i+1, "tr").nl(i+2); - out.sTag(i+2, "th").append("key").eTag("th").nl(i+3); - out.sTag(i+2, "th").append("value").eTag("th").nl(i+3); - out.ie(i+1).eTag("tr").nl(i+2); - } - for (Map.Entry e : (Set<Map.Entry>)m.entrySet()) { - - Object key = session.generalize(e.getKey(), keyType); - Object value = null; - try { - value = e.getValue(); - } catch (StackOverflowError t) { - throw t; - } catch (Throwable t) { - session.onError(t, "Could not call getValue() on property ''{0}'', {1}", e.getKey(), t.getLocalizedMessage()); - } - - String link = getLink(ppMeta); - String style = getStyle(session, ppMeta, value); - - out.sTag(i+1, "tr").nl(i+2); - out.oTag(i+2, "td"); - if (style != null) - out.attr("style", style); - out.cTag(); - if (link != null) - out.oTag(i+3, "a").attrUri("href", link.replace("{#}", StringUtils.toString(value))).cTag(); - ContentResult cr = serializeAnything(session, out, key, keyType, null, 2, null, false); - if (link != null) - out.eTag("a"); - if (cr == CR_NORMAL) - out.i(i+2); - out.eTag("td").nl(i+2); - out.sTag(i+2, "td"); - cr = serializeAnything(session, out, value, valueType, (key == null ? "_x0000_" : session.toString(key)), 2, null, false); - if (cr == CR_NORMAL) - out.ie(i+2); - out.eTag("td").nl(i+2); - out.ie(i+1).eTag("tr").nl(i+1); - } - out.ie(i).eTag("table").nl(i); - } - - private void serializeBeanMap(HtmlSerializerSession session, HtmlWriter out, BeanMap<?> m, ClassMeta<?> eType, - BeanPropertyMeta ppMeta) throws Exception { - int i = session.getIndent(); - - out.oTag(i, "table"); - - String typeName = m.getMeta().getDictionaryName(); - if (typeName != null && eType != m.getClassMeta()) - out.attr(session.getBeanTypePropertyName(m.getClassMeta()), typeName); - - out.append('>').nl(i); - if (session.isAddKeyValueTableHeaders() && ! (m.getClassMeta().getExtendedMeta(HtmlClassMeta.class).isNoTableHeaders() - || (ppMeta != null && ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isNoTableHeaders()))) { - out.sTag(i+1, "tr").nl(i+1); - out.sTag(i+2, "th").append("key").eTag("th").nl(i+2); - out.sTag(i+2, "th").append("value").eTag("th").nl(i+2); - out.ie(i+1).eTag("tr").nl(i+1); - } - - for (BeanPropertyValue p : m.getValues(session.isTrimNulls())) { - BeanPropertyMeta pMeta = p.getMeta(); - ClassMeta<?> cMeta = p.getClassMeta(); - - String key = p.getName(); - Object value = p.getValue(); - Throwable t = p.getThrown(); - if (t != null) - session.onBeanGetterException(pMeta, t); - - if (session.canIgnoreValue(cMeta, key, value)) - continue; - - String link = cMeta.isCollectionOrArray() ? null : getLink(pMeta); - - out.sTag(i+1, "tr").nl(i+1); - out.sTag(i+2, "td").text(key).eTag("td").nl(i+2); - out.oTag(i+2, "td"); - String style = getStyle(session, pMeta, value); - if (style != null) - out.attr("style", style); - out.cTag(); - - try { - if (link != null) - out.oTag(i+3, "a").attrUri("href", m.resolveVars(link)).cTag(); - ContentResult cr = serializeAnything(session, out, value, cMeta, key, 2, pMeta, false); - if (cr == CR_NORMAL) - out.i(i+2); - if (link != null) - out.eTag("a"); - } catch (SerializeException e) { - throw e; - } catch (Error e) { - throw e; - } catch (Throwable e) { - e.printStackTrace(); - session.onBeanGetterException(pMeta, e); - } - out.eTag("td").nl(i+2); - out.ie(i+1).eTag("tr").nl(i+1); - } - out.ie(i).eTag("table").nl(i); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private void serializeCollection(HtmlSerializerSession session, HtmlWriter out, Object in, ClassMeta<?> sType, - ClassMeta<?> eType, String name, BeanPropertyMeta ppMeta) throws Exception { - - ClassMeta<?> seType = sType.getElementType(); - if (seType == null) - seType = session.object(); - - Collection c = (sType.isCollection() ? (Collection)in : toList(sType.getInnerClass(), in)); - - int i = session.getIndent(); - if (c.isEmpty()) { - out.appendln(i, "<ul></ul>"); - return; - } - - String type2 = null; - if (sType != eType) - type2 = sType.getDictionaryName(); - if (type2 == null) - type2 = "array"; - - c = session.sort(c); - - HtmlBeanPropertyMeta hbpMeta = (ppMeta == null ? null : ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class)); - String btpn = session.getBeanTypePropertyName(eType); - - // Look at the objects to see how we're going to handle them. Check the first object to see how we're going to - // handle this. - // If it's a map or bean, then we'll create a table. - // Otherwise, we'll create a list. - Object[] th = getTableHeaders(session, c, hbpMeta); - - if (th != null) { - - out.oTag(i, "table").attr(btpn, type2).append('>').nl(i+1); - out.sTag(i+1, "tr").nl(i+2); - for (Object key : th) { - out.sTag(i+2, "th"); - out.text(session.convertToType(key, String.class)); - out.eTag("th").nl(i+2); - } - out.ie(i+1).eTag("tr").nl(i+1); - - for (Object o : c) { - ClassMeta<?> cm = session.getClassMetaForObject(o); - - if (cm != null && cm.getPojoSwap() != null) { - PojoSwap f = cm.getPojoSwap(); - o = f.swap(session, o); - cm = cm.getSerializedClassMeta(); - } - - out.oTag(i+1, "tr"); - String typeName = (cm == null ? null : cm.getDictionaryName()); - String typeProperty = session.getBeanTypePropertyName(cm); - - if (typeName != null && eType.getElementType() != cm) - out.attr(typeProperty, typeName); - out.cTag().nl(i+2); - - if (cm == null) { - serializeAnything(session, out, o, null, null, 1, null, false); - - } else if (cm.isMap() && ! (cm.isBeanMap())) { - Map m2 = session.sort((Map)o); - - for (Object k : th) { - out.sTag(i+2, "td"); - ContentResult cr = serializeAnything(session, out, m2.get(k), eType.getElementType(), session.toString(k), 2, null, false); - if (cr == CR_NORMAL) - out.i(i+2); - out.eTag("td").nl(i+2); - } - } else { - BeanMap m2 = null; - if (o instanceof BeanMap) - m2 = (BeanMap)o; - else - m2 = session.toBeanMap(o); - - for (Object k : th) { - BeanMapEntry p = m2.getProperty(session.toString(k)); - BeanPropertyMeta pMeta = p.getMeta(); - String link = pMeta.getClassMeta().isCollectionOrArray() ? null : getLink(pMeta); - Object value = p.getValue(); - String style = getStyle(session, pMeta, value); - out.oTag(i+2, "td"); - if (style != null) - out.attr("style", style); - out.cTag(); - if (link != null) - out.oTag("a").attrUri("href", m2.resolveVars(link)).cTag(); - ContentResult cr = serializeAnything(session, out, value, pMeta.getClassMeta(), p.getKey().toString(), 2, pMeta, false); - if (cr == CR_NORMAL) - out.i(i+2); - if (link != null) - out.eTag("a"); - out.eTag("td").nl(i+2); - } - } - out.ie(i+1).eTag("tr").nl(i+1); - } - out.ie(i).eTag("table").nl(i); - - } else { - out.oTag(i, "ul"); - if (! type2.equals("array")) - out.attr(btpn, type2); - out.append('>').nl(i+1); - for (Object o : c) { - out.oTag(i+1, "li"); - String style = getStyle(session, ppMeta, o); - String link = getLink(ppMeta); - if (style != null) - out.attr("style", style); - out.cTag(); - if (link != null) - out.oTag(i+2, "a").attrUri("href", link.replace("{#}", StringUtils.toString(o))).cTag(); - ContentResult cr = serializeAnything(session, out, o, eType.getElementType(), name, 1, null, false); - if (link != null) - out.eTag("a"); - if (cr == CR_NORMAL) - out.ie(i+1); - out.eTag("li").nl(i+1); - } - out.ie(i).eTag("ul").nl(i); - } - } - - private static HtmlRender<?> getRender(HtmlSerializerSession session, BeanPropertyMeta pMeta, Object value) { - if (pMeta == null) - return null; - HtmlBeanPropertyMeta hpMeta = pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class); - HtmlRender<?> render = hpMeta.getRender(); - if (render != null) - return render; - ClassMeta<?> cMeta = session.getClassMetaForObject(value); - render = cMeta == null ? null : cMeta.getExtendedMeta(HtmlClassMeta.class).getRender(); - return render; - } - - @SuppressWarnings({"rawtypes","unchecked"}) - private static String getStyle(HtmlSerializerSession session, BeanPropertyMeta pMeta, Object value) { - HtmlRender render = getRender(session, pMeta, value); - return render == null ? null : render.getStyle(session, value); - } - - private static String getLink(BeanPropertyMeta pMeta) { - return pMeta == null ? null : pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).getLink(); - } - - /* - * Returns the table column headers for the specified collection of objects. - * Returns null if collection should not be serialized as a 2-dimensional table. - * 2-dimensional tables are used for collections of objects that all have the same set of property names. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static Object[] getTableHeaders(SerializerSession session, Collection c, HtmlBeanPropertyMeta hbpMeta) throws Exception { - if (c.size() == 0) - return null; - c = session.sort(c); - Object[] th; - Set<ClassMeta> prevC = new HashSet<ClassMeta>(); - Object o1 = null; - for (Object o : c) - if (o != null) { - o1 = o; - break; - } - if (o1 == null) - return null; - ClassMeta<?> cm = session.getClassMetaForObject(o1); - if (cm.getPojoSwap() != null) { - PojoSwap f = cm.getPojoSwap(); - o1 = f.swap(session, o1); - cm = cm.getSerializedClassMeta(); - } - if (cm == null || ! cm.isMapOrBean()) - return null; - if (cm.getInnerClass().isAnnotationPresent(HtmlLink.class)) - return null; - HtmlClassMeta h = cm.getExtendedMeta(HtmlClassMeta.class); - if (h.isNoTables() || (hbpMeta != null && hbpMeta.isNoTables())) - return null; - if (h.isNoTableHeaders() || (hbpMeta != null && hbpMeta.isNoTableHeaders())) - return new Object[0]; - if (session.canIgnoreValue(cm, null, o1)) - return null; - if (cm.isMap() && ! cm.isBeanMap()) { - Set<Object> set = new LinkedHashSet<Object>(); - for (Object o : c) { - if (! session.canIgnoreValue(cm, null, o)) { - if (! cm.isInstance(o)) - return null; - Map m = session.sort((Map)o); - for (Map.Entry e : (Set<Map.Entry>)m.entrySet()) { - if (e.getValue() != null) - set.add(e.getKey() == null ? null : e.getKey()); - } - } - } - th = set.toArray(new Object[set.size()]); - } else { - Map<String,Boolean> m = new LinkedHashMap<String,Boolean>(); - for (Object o : c) { - if (! session.canIgnoreValue(cm, null, o)) { - if (! cm.isInstance(o)) - return null; - BeanMap<?> bm = (o instanceof BeanMap ? (BeanMap)o : session.toBeanMap(o)); - for (Map.Entry<String,Object> e : bm.entrySet()) { - String key = e.getKey(); - if (e.getValue() != null) - m.put(key, true); - else if (! m.containsKey(key)) - m.put(key, false); - } - } - } - for (Iterator<Boolean> i = m.values().iterator(); i.hasNext();) - if (! i.next()) - i.remove(); - th = m.keySet().toArray(new Object[m.size()]); - } - prevC.add(cm); - boolean isSortable = true; - for (Object o : th) - isSortable &= (o instanceof Comparable); - Set<Object> s = (isSortable ? new TreeSet<Object>() : new LinkedHashSet<Object>()); - s.addAll(Arrays.asList(th)); - - for (Object o : c) { - if (o == null) - continue; - cm = session.getClassMetaForObject(o); - if (cm != null && cm.getPojoSwap() != null) { - PojoSwap f = cm.getPojoSwap(); - o = f.swap(session, o); - cm = cm.getSerializedClassMeta(); - } - if (prevC.contains(cm)) - continue; - if (cm == null || ! (cm.isMap() || cm.isBean())) - return null; - if (cm.getInnerClass().isAnnotationPresent(HtmlLink.class)) - return null; - if (session.canIgnoreValue(cm, null, o)) - return null; - if (cm.isMap() && ! cm.isBeanMap()) { - Map m = (Map)o; - if (th.length != m.keySet().size()) - return null; - for (Object k : m.keySet()) - if (! s.contains(k.toString())) - return null; - } else { - BeanMap<?> bm = (o instanceof BeanMap ? (BeanMap)o : session.toBeanMap(o)); - int l = 0; - for (String k : bm.keySet()) { - if (! s.contains(k)) - return null; - l++; - } - if (s.size() != l) - return null; - } - } - return th; - } - - /** - * Returns the schema serializer based on the settings of this serializer. - * - * @return The schema serializer. - */ @Override /* XmlSerializer */ public HtmlSerializer getSchemaSerializer() { if (schemaSerializer == null) - schemaSerializer = new HtmlSchemaDocSerializer(propertyStore, getOverrideProperties()); + schemaSerializer = new HtmlSchemaDocSerializer(propertyStore); return schemaSerializer; } - - //-------------------------------------------------------------------------------- - // Entry point methods - //-------------------------------------------------------------------------------- - - @Override /* Serializer */ - public HtmlSerializerSession createSession(ObjectMap op, Method javaMethod, Locale locale, - TimeZone timeZone, MediaType mediaType, UriContext uriContext) { - return new HtmlSerializerSession(ctx, op, javaMethod, locale, timeZone, mediaType, uriContext); - } - @Override /* Serializer */ - protected void doSerialize(SerializerSession session, SerializerOutput out, Object o) throws Exception { - HtmlSerializerSession s = (HtmlSerializerSession)session; - doSerialize(s, o, s.getHtmlWriter(out)); + public WriterSerializerSession createSession(SerializerSessionArgs args) { + return new HtmlSerializerSession(ctx, args); } }
