http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParser.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParser.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParser.java new file mode 100755 index 0000000..cf96164 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParser.java @@ -0,0 +1,743 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.html; + +import static com.ibm.juno.core.html.HtmlParser.Tag.*; +import static com.ibm.juno.core.utils.StringUtils.*; +import static javax.xml.stream.XMLStreamConstants.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import javax.xml.namespace.*; +import javax.xml.stream.*; +import javax.xml.stream.events.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.filter.*; +import com.ibm.juno.core.parser.*; + +/** + * Parses text generated by the {@link HtmlSerializer} class back into a POJO model. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Content-Type</code> types: <code>text/html</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * See the {@link HtmlSerializer} class for a description of the HTML generated. + * <p> + * This class is used primarily for automated testing of the {@link HtmlSerializer} class. + * + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link ParserProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * + * @author James Bognar ([email protected]) + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +@Consumes({"text/html","text/html+stripped"}) +public final class HtmlParser extends ReaderParser { + + /** Default parser, all default settings.*/ + public static final HtmlParser DEFAULT = new HtmlParser().lock(); + + /** HTML specific properties currently defined on this class */ + protected transient HtmlParserProperties hpp = new HtmlParserProperties(); + + /* + * Reads anything starting at the current event. + * <p> + * Precondition: Must be pointing at START_ELEMENT or CHARACTERS event. + * Postcondition: Pointing at next event to be processed. + */ + private <T> T parseAnything(HtmlParserContext ctx, ClassMeta<T> nt, XMLEventReader r, Object outer, Object name) throws ParseException, IOException { + + try { + BeanContext bc = ctx.getBeanContext(); + if (nt == null) + nt = (ClassMeta<T>)object(); + PojoFilter<T,Object> filter = (PojoFilter<T,Object>)nt.getPojoFilter(); + ClassMeta<?> ft = nt.getFilteredClassMeta(); + + Object o = null; + + XMLEvent event = r.nextEvent(); + while (! (event.isStartElement() || (event.isCharacters() && ! event.asCharacters().isWhiteSpace()) || event.isEndDocument())) + event = r.nextEvent(); + + if (event.isEndDocument()) + throw new XMLStreamException("Unexpected end of stream in parseAnything for type '"+nt+"'", event.getLocation()); + + if (event.isCharacters()) { + String text = parseCharacters(event, r); + if (ft.isObject()) + o = text; + else if (ft.isCharSequence()) + o = text; + else if (ft.isNumber()) + o = parseNumber(text, (Class<? extends Number>)nt.getInnerClass()); + else if (ft.isChar()) + o = text.charAt(0); + else if (ft.isBoolean()) + o = Boolean.parseBoolean(text); + else if (ft.canCreateNewInstanceFromString(outer)) + o = ft.newInstanceFromString(outer, text); + else + throw new XMLStreamException("Unexpected characters '"+event.asCharacters().getData()+"' for type '"+nt+"'", event.getLocation()); + + } else { + Tag tag = Tag.forString(event.asStartElement().getName().getLocalPart(), false); + String tableType = "object"; + String text = ""; + + if (tag.isOneOf(STRING, NUMBER, BOOLEAN, BR, FF, BS, TB)) + text = parseCharacters(event, r); + + if (tag == TABLE) { + Map<String,String> attrs = getAttributes(event); + tableType = attrs.get("type"); + String c = attrs.get("_class"); + if (c != null) + ft = nt = (ClassMeta<T>)bc.getClassMetaFromString(c); + } + + boolean isValid = true; + + if (tag == NULL) + nextTag(r, xNULL); + else if (tag == A) + o = parseAnchor(ctx, event, r, nt); + else if (ft.isObject()) { + if (tag == STRING) + o = text; + else if (tag == NUMBER) + o = parseNumber(text, null); + else if (tag == BOOLEAN) + o = Boolean.parseBoolean(text); + else if (tag == TABLE) { + if (tableType.equals("object")) { + o = parseIntoMap(ctx, r, (Map)new ObjectMap(bc), ft.getKeyType(), ft.getValueType()); + } else if (tableType.equals("array")) { + o = parseTableIntoCollection(ctx, r, (Collection)new ObjectList(bc), ft.getElementType()); + } else + isValid = false; + } + else if (tag == UL) + o = parseIntoCollection(ctx, r, new ObjectList(bc), null); + } + else if (tag == STRING && ft.isCharSequence()) + o = text; + else if (tag == STRING && ft.isChar()) + o = text.charAt(0); + else if (tag == STRING && ft.canCreateNewInstanceFromString(outer)) + o = ft.newInstanceFromString(outer, text); + else if (tag == NUMBER && ft.isNumber()) + o = parseNumber(text, (Class<? extends Number>)ft.getInnerClass()); + else if (tag == BOOLEAN && ft.isBoolean()) + o = Boolean.parseBoolean(text); + else if (tag == TABLE) { + if (tableType.equals("object")) { + if (ft.isMap()) { + o = parseIntoMap(ctx, r, (Map)(ft.canCreateNewInstance(outer) ? ft.newInstance(outer) : new ObjectMap(bc)), ft.getKeyType(), ft.getValueType()); + } else if (ft.canCreateNewInstanceFromObjectMap(outer)) { + ObjectMap m = new ObjectMap(bc); + parseIntoMap(ctx, r, m, string(), object()); + o = ft.newInstanceFromObjectMap(outer, m); + } else if (ft.canCreateNewBean(outer)) { + BeanMap m = bc.newBeanMap(outer, ft.getInnerClass()); + o = parseIntoBean(ctx, r, m).getBean(); + } + else + isValid = false; + } else if (tableType.equals("array")) { + if (ft.isCollection()) + o = parseTableIntoCollection(ctx, r, (Collection)(ft.canCreateNewInstance(outer) ? ft.newInstance(outer) : new ObjectList(bc)), ft.getElementType()); + else if (ft.isArray()) + o = bc.toArray(ft, parseTableIntoCollection(ctx, r, new ArrayList(), ft.getElementType())); + else + isValid = false; + } else + isValid = false; + } else if (tag == UL) { + if (ft.isCollection()) + o = parseIntoCollection(ctx, r, (Collection)(ft.canCreateNewInstance(outer) ? ft.newInstance(outer) : new ObjectList(bc)), ft.getElementType()); + else if (ft.isArray()) + o = bc.toArray(ft, parseIntoCollection(ctx, r, new ArrayList(), ft.getElementType())); + else + isValid = false; + } else + isValid = false; + + if (! isValid) + throw new XMLStreamException("Unexpected tag '"+tag+"' for type '"+nt+"'", event.getLocation()); + } + + + if (filter != null && o != null) + o = filter.unfilter(o, nt); + + if (outer != null) + setParent(nt, o, outer); + + if (name != null) + setName(nt, o, name); + + return (T)o; + + } catch (ParseException e) { + throw e; + } catch (IOException e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new ParseException(e); + } + } + + /* + * Reads an anchor tag and converts it into a bean. + */ + private <T> T parseAnchor(HtmlParserContext ctx, XMLEvent e, XMLEventReader r, ClassMeta<T> beanType) throws XMLStreamException { + BeanContext bc = ctx.getBeanContext(); + String href = e.asStartElement().getAttributeByName(new QName("href")).getValue(); + String name = parseCharacters(e, r); + Class<T> beanClass = beanType.getInnerClass(); + if (beanClass.isAnnotationPresent(HtmlLink.class)) { + HtmlLink h = beanClass.getAnnotation(HtmlLink.class); + BeanMap<T> m = bc.newBeanMap(beanClass); + m.put(h.hrefProperty(), href); + m.put(h.nameProperty(), name); + return m.getBean(); + } + return bc.convertToType(href, beanType); + } + + private Map<String,String> getAttributes(XMLEvent e) { + Map<String,String> m = new TreeMap<String,String>() ; + for (Iterator i = e.asStartElement().getAttributes(); i.hasNext();) { + Attribute a = (Attribute)i.next(); + m.put(a.getName().getLocalPart(), a.getValue()); + } + 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(HtmlParserContext ctx, XMLEventReader r, Map<K,V> m, ClassMeta<K> keyType, ClassMeta<V> valueType) throws ParseException, IOException { + try { + Tag tag = nextTag(r, TR); + + // Skip over the column headers. + nextTag(r, TH); + parseElementText(r, xTH); + nextTag(r, TH); + parseElementText(r, xTH); + nextTag(r, xTR); + + while (true) { + tag = nextTag(r, TR, xTABLE); + if (tag == xTABLE) + break; + nextTag(r, TD); + K key = parseAnything(ctx, keyType, r, m, null); + nextTag(r, xTD); + nextTag(r, TD); + m.put(key, parseAnything(ctx, valueType, r, m, key)); + nextTag(r, xTD); + nextTag(r, xTR); + } + + return m; + } catch (XMLStreamException e) { + throw new ParseException(e); + } + } + + /* + * 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(HtmlParserContext ctx, XMLEventReader r, Collection<E> l, ClassMeta<E> elementType) throws ParseException, IOException { + try { + while (true) { + Tag tag = nextTag(r, LI, xUL); + if (tag == xUL) + break; + l.add(parseAnything(ctx, elementType, r, l, null)); + nextTag(r, xLI); + } + return l; + } catch (XMLStreamException e) { + throw new ParseException(e); + } + } + + /* + * Reads contents of <ul> element into an Object array. + * Precondition: Must be pointing at event following <ul> event. + * Postcondition: Pointing at next START_ELEMENT or END_DOCUMENT event. + */ + private Object[] parseArgs(HtmlParserContext ctx, XMLEventReader r, ClassMeta<?>[] argTypes) throws ParseException, IOException { + try { + Object[] o = new Object[argTypes.length]; + int i = 0; + while (true) { + Tag tag = nextTag(r, LI, xUL); + if (tag == xUL) + break; + o[i] = parseAnything(ctx, argTypes[i], r, ctx.getOuter(), null); + i++; + nextTag(r, xLI); + } + return o; + } catch (XMLStreamException e) { + throw new ParseException(e); + } + } + + /* + * 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(HtmlParserContext ctx, XMLEventReader r, Collection<E> l, ClassMeta<E> elementType) throws Exception { + + BeanContext bc = ctx.getBeanContext(); + if (elementType == null) + elementType = (ClassMeta<E>)object(); + + Tag tag = nextTag(r, TR); + List<String> keys = new ArrayList<String>(); + while (true) { + tag = nextTag(r, TH, xTR); + if (tag == xTR) + break; + keys.add(parseElementText(r, xTH)); + } + + while (true) { + XMLEvent event = r.nextTag(); + tag = Tag.forEvent(event); + if (tag == xTABLE) + break; + if (elementType.canCreateNewBean(l)) { + BeanMap m = bc.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(ctx, object(), r, l, null); + } else { + e.getMeta().set(m, parseAnything(ctx, e.getMeta().getClassMeta(), r, m.getBean(false), key)); + } + nextTag(r, xTD); + } + l.add(m == null ? null : (E)m.getBean()); + } else { + String c = getAttributes(event).get("_class"); + Map m = (Map)(elementType.isMap() && elementType.canCreateNewInstance(l) ? elementType.newInstance(l) : new ObjectMap(bc)); + 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) + m.put(key, parseAnything(ctx, elementType.getElementType(), r, l, key)); + nextTag(r, xTD); + } + if (m != null && c != null) { + ObjectMap m2 = (m instanceof ObjectMap ? (ObjectMap)m : new ObjectMap(m).setBeanContext(ctx.getBeanContext())); + m2.put("_class", c); + l.add((E)m2.cast()); + } 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(HtmlParserContext ctx, XMLEventReader r, BeanMap<T> m) throws Exception { + Tag tag = nextTag(r, TR); + + // Skip over the column headers. + nextTag(r, TH); + parseElementText(r, xTH); + nextTag(r, TH); + parseElementText(r, xTH); + nextTag(r, xTR); + + while (true) { + tag = nextTag(r, TR, xTABLE); + if (tag == xTABLE) + break; + nextTag(r, TD); + String key = parseElementText(r, xTD); + nextTag(r, TD); + BeanPropertyMeta pMeta = m.getPropertyMeta(key); + if (pMeta == null) { + if (m.getMeta().isSubTyped()) { + m.put(key, parseAnything(ctx, object(), r, m.getBean(false), key)); + } else { + onUnknownProperty(ctx, key, m, -1, -1); + parseAnything(ctx, object(), r, null, null); + } + } else { + pMeta.set(m, parseAnything(ctx, pMeta.getClassMeta(), r, m.getBean(false), key)); + } + nextTag(r, xTD); + nextTag(r, xTR); + } + return m; + } + + /* + * Parse until the next event is an end tag. + */ + private String parseCharacters(XMLEvent e, XMLEventReader r) throws XMLStreamException { + + List<String> strings = new LinkedList<String>(); + + while (true) { + int eventType = e.getEventType(); + if (eventType == CHARACTERS) { + Characters c = e.asCharacters(); + if (! c.isWhiteSpace()) + strings.add(c.getData()); + } + else if (eventType == START_ELEMENT) { + Tag tag = Tag.forEvent(e); + if (tag == BR) + strings.add("\n"); + else if (tag == FF) + strings.add("\f"); + else if (tag == BS) + strings.add("\b"); + else if (tag == TB) + strings.add("\t"); + } + // Ignore all other elements. + + XMLEvent eNext = r.peek(); + + if (eNext.isStartElement() || eNext.isEndElement()) { + Tag tag = Tag.forEvent(eNext); + if (! (tag.isOneOf(A, xA, BR, xBR, FF, xFF, BS, xBS, TB, xTB, STRING, xSTRING, NUMBER, xNUMBER, BOOLEAN, xBOOLEAN))) + return trim(join(strings)); + } else if (eNext.isEndDocument()) { + return trim(join(strings)); + } + + e = r.nextEvent(); + } + } + + private String trim(String s) { + int i2 = 0, i3; + for (i2 = 0; i2 < s.length(); i2++) { + char c = s.charAt(i2); + if (c != ' ') + break; + } + for (i3 = s.length(); i3 > i2; i3--) { + char c = s.charAt(i3-1); + if (c != ' ') + break; + } + return s.substring(i2, i3); + } + + /* + * Reads the element text of the current element, accounting for <a> and <br> tags. <br> + * Precondition: Must be pointing at first event AFTER the start tag. + * Postcondition: Pointing at next START_ELEMENT or END_DOCUMENT event. + */ + private String parseElementText(XMLEventReader r, Tag endTag) throws XMLStreamException { + + List<String> strings = new LinkedList<String>(); + + XMLEvent e = r.nextEvent(); + Tag nTag = (e.isEndElement() ? Tag.forEvent(e) : null); + + while (nTag != endTag) { + if (e.isCharacters()) + strings.add(parseCharacters(e, r)); + e = r.nextEvent(); + + if (e.getEventType() == END_ELEMENT) + nTag = Tag.forEvent(e); + + if (nTag == endTag) + return join(strings); + } + + return ""; + } + + enum Tag { + + TABLE(1,"<table>"), + TR(2,"<tr>"), + TH(3,"<th>"), + TD(4,"<td>"), + UL(5,"<ul>"), + LI(6,"<li>"), + STRING(7,"<string>"), + NUMBER(8,"<number>"), + BOOLEAN(9,"<boolean>"), + NULL(10,"<null>"), + A(11,"<a>"), + BR(12,"<br>"), // newline + FF(13,"<ff>"), // formfeed + BS(14,"<bs>"), // backspace + TB(15,"<tb>"), // tab + xTABLE(-1,"</table>"), + xTR(-2,"</tr>"), + xTH(-3,"</th>"), + xTD(-4,"</td>"), + xUL(-5,"</ul>"), + xLI(-6,"</li>"), + xSTRING(-7,"</string>"), + xNUMBER(-8,"</number>"), + xBOOLEAN(-9,"</boolean>"), + xNULL(-10,"</null>"), + xA(-11,"</a>"), + xBR(-12,"</br>"), + xFF(-13,"</ff>"), + xBS(-14,"</bs>"), + xTB(-15,"</tb>"); + + private Map<Integer,Tag> cache = new HashMap<Integer,Tag>(); + + int id; + String label; + + Tag(int id, String label) { + this.id = id; + this.label = label; + cache.put(id, this); + } + + static Tag forEvent(XMLEvent event) throws XMLStreamException { + if (event.isStartElement()) + return forString(event.asStartElement().getName().getLocalPart(), false); + else if (event.isEndElement()) + return forString(event.asEndElement().getName().getLocalPart(), true); + throw new XMLStreamException("Invalid call to Tag.forEvent on event of type ["+event.getEventType()+"]"); + } + + private static Tag forString(String tag, boolean end) throws XMLStreamException { + char c = tag.charAt(0); + Tag t = null; + if (c == 'u') + t = (end ? xUL : UL); + else if (c == 'l') + t = (end ? xLI : LI); + else if (c == 's') + t = (end ? xSTRING : STRING); + else if (c == 'b') { + c = tag.charAt(1); + if (c == 'o') + t = (end ? xBOOLEAN : BOOLEAN); + else if (c == 'r') + t = (end ? xBR : BR); + else if (c == 's') + t = (end ? xBS : BS); + } + else if (c == 'a') + t = (end ? xA : A); + else if (c == 'n') { + c = tag.charAt(2); + if (c == 'm') + t = (end ? xNUMBER : NUMBER); + else if (c == 'l') + t = (end ? xNULL : NULL); + } + else if (c == 't') { + c = tag.charAt(1); + if (c == 'a') + t = (end ? xTABLE : TABLE); + else if (c == 'r') + t = (end ? xTR : TR); + else if (c == 'h') + t = (end ? xTH : TH); + else if (c == 'd') + t = (end ? xTD : TD); + else if (c == 'b') + t = (end ? xTB : TB); + } + else if (c == 'f') + t = (end ? xFF : FF); + if (t == null) + throw new XMLStreamException("Unknown tag '"+tag+"' encountered"); + return t; + } + + @Override /* Object */ + public String toString() { + return label; + } + + public boolean isOneOf(Tag...tags) { + for (Tag tag : tags) + if (tag == this) + return true; + return false; + } + } + + /* + * Reads the current 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 Tag nextTag(XMLEventReader r, Tag...expected) throws XMLStreamException { + XMLEvent event = r.nextTag(); + Tag tag = Tag.forEvent(event); + if (expected.length == 0) + return tag; + for (Tag t : expected) + if (t == tag) + return tag; + throw new XMLStreamException("Unexpected tag: " + tag, event.getLocation()); + } + + private String join(List<String> s) { + if (s.size() == 0) + return ""; + if (s.size() == 1) + return s.get(0); + StringBuilder sb = new StringBuilder(); + for (String ss : s) + sb.append(ss); + return sb.toString(); + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Parser */ + public HtmlParserContext createContext(ObjectMap properties, Method javaMethod, Object outer) { + return new HtmlParserContext(getBeanContext(), pp, hpp, properties, javaMethod, outer); + } + + @Override /* Parser */ + protected <T> T doParse(Reader in, int estimatedSize, ClassMeta<T> type, ParserContext ctx) throws ParseException, IOException { + type = ctx.getBeanContext().normalizeClassMeta(type); + HtmlParserContext ctx2 = (HtmlParserContext)ctx; + return parseAnything(ctx2, type, ctx2.getReader(in, estimatedSize), ctx.getOuter(), null); + } + + @Override /* ReaderParser */ + protected <K,V> Map<K,V> doParseIntoMap(Reader in, int estimatedSize, Map<K,V> m, Type keyType, Type valueType, ParserContext ctx) throws ParseException, IOException { + HtmlParserContext hctx = (HtmlParserContext)ctx; + return parseIntoMap(hctx, hctx.getReader(in, estimatedSize), m, ctx.getBeanContext().getClassMeta(keyType), ctx.getBeanContext().getClassMeta(valueType)); + } + + @Override /* ReaderParser */ + protected <E> Collection<E> doParseIntoCollection(Reader in, int estimatedSize, Collection<E> c, Type elementType, ParserContext ctx) throws ParseException, IOException { + HtmlParserContext hctx = (HtmlParserContext)ctx; + return parseIntoCollection(hctx, hctx.getReader(in, estimatedSize), c, ctx.getBeanContext().getClassMeta(elementType)); + } + + @Override /* ReaderParser */ + protected Object[] doParseArgs(Reader in, int estimatedSize, ClassMeta<?>[] argTypes, ParserContext ctx) throws ParseException, IOException { + HtmlParserContext hctx = (HtmlParserContext)ctx; + return parseArgs(hctx, hctx.getReader(in, estimatedSize), argTypes); + } + + @Override /* CoreApi */ + public HtmlParser setProperty(String property, Object value) throws LockedException { + checkLock(); + if (! hpp.setProperty(property, value)) + super.setProperty(property, value); + return this; + } + + @Override /* CoreApi */ + public HtmlParser setProperties(ObjectMap properties) throws LockedException { + for (Map.Entry<String,Object> e : properties.entrySet()) + setProperty(e.getKey(), e.getValue()); + return this; + } + + @Override /* CoreApi */ + public HtmlParser addNotBeanClasses(Class<?>...classes) throws LockedException { + super.addNotBeanClasses(classes); + return this; + } + + @Override /* CoreApi */ + public HtmlParser addFilters(Class<?>...classes) throws LockedException { + super.addFilters(classes); + return this; + } + + @Override /* CoreApi */ + public <T> HtmlParser addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + super.addImplClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreApi */ + public HtmlParser setClassLoader(ClassLoader classLoader) throws LockedException { + super.setClassLoader(classLoader); + return this; + } + + @Override /* Lockable */ + public HtmlParser lock() { + super.lock(); + return this; + } + + @Override /* Lockable */ + public HtmlParser clone() { + try { + return (HtmlParser)super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Shouldn't happen + } + } +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserContext.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserContext.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserContext.class new file mode 100755 index 0000000..00ee7b4 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserContext.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserContext.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserContext.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserContext.java new file mode 100755 index 0000000..b23546e --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserContext.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * Note to U.S. Government Users Restricted Rights: Use, + * duplication or disclosure restricted by GSA ADP Schedule + * Contract with IBM Corp. + *******************************************************************************/ +package com.ibm.juno.core.html; + +import java.io.*; +import java.lang.reflect.*; + +import javax.xml.stream.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.parser.*; +import com.ibm.juno.core.utils.*; + +/** + * Context object that lives for the duration of a single parsing of {@link HtmlParser}. + * <p> + * + * @author James Bognar ([email protected]) + */ +public final class HtmlParserContext extends ParserContext { + + private XMLEventReader xmlEventReader; + + /** + * Create a new parser context with the specified options. + * + * @param beanContext The bean context being used. + * @param pp The default generic parser properties. + * @param hpp The default HTML parser properties. + * @param properties The override properties. + * @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. + */ + public HtmlParserContext(BeanContext beanContext, ParserProperties pp, HtmlParserProperties hpp, ObjectMap properties, Method javaMethod, Object outer) { + super(beanContext, pp, properties, javaMethod, outer); + } + + /** + * Wraps the specified reader in an {@link XMLEventReader}. + * This event reader gets closed by the {@link #close()} method. + * + * @param in The reader to read from. + * @param estimatedSize The estimated size of the input. If <code>-1</code>, uses a default size of <code>8196</code>. + * @return A new XML event reader using a new {@link XMLInputFactory}. + * @throws ParseException + */ + protected XMLEventReader getReader(Reader in, int estimatedSize) throws ParseException { + try { + in = IOUtils.getBufferedReader(in, estimatedSize); + XMLInputFactory factory = XMLInputFactory.newInstance(); + factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false); + this.xmlEventReader = factory.createXMLEventReader(in); + } catch (Error e) { + throw new ParseException(e.getLocalizedMessage()); + } catch (XMLStreamException e) { + throw new ParseException(e); + } + return xmlEventReader; + } + + @Override /* ParserContext */ + public void close() throws ParseException { + if (xmlEventReader != null) { + try { + xmlEventReader.close(); + } catch (XMLStreamException e) { + throw new ParseException(e); + } + } + super.close(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserProperties.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserProperties.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserProperties.class new file mode 100755 index 0000000..9fb0222 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserProperties.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserProperties.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserProperties.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserProperties.java new file mode 100755 index 0000000..5d2ff60 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlParserProperties.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.html; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.parser.*; + +/** + * Configurable properties on the {@link HtmlParser} class. + * <p> + * Use the {@link HtmlParser#setProperty(String, Object)} method to set property values. + * <p> + * In addition to these properties, the following properties are also applicable for {@link HtmlParser}. + * <ul> + * <li>{@link ParserProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * @author James Bognar ([email protected]) + */ +public final class HtmlParserProperties implements Cloneable { + + /** + * Sets the specified property value. + * + * @param property The property name. + * @param value The property value. + * @return <jk>true</jk> if property name was valid and property was set. + */ + protected boolean setProperty(String property, Object value) { + return false; + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Lockable */ + public HtmlParserProperties clone() { + try { + return (HtmlParserProperties)super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Shouldn't happen. + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSchemaDocSerializer.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSchemaDocSerializer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSchemaDocSerializer.class new file mode 100755 index 0000000..b25c1fa Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSchemaDocSerializer.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSchemaDocSerializer.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSchemaDocSerializer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSchemaDocSerializer.java new file mode 100755 index 0000000..2cf8d95 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSchemaDocSerializer.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.html; + +import static com.ibm.juno.core.serializer.SerializerProperties.*; +import static com.ibm.juno.core.utils.ClassUtils.*; + +import java.io.*; +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.filter.*; +import com.ibm.juno.core.serializer.*; + +/** + * Serializes POJO metamodels to HTML. + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Accept</code> types: <code>text/html+schema</code> + * <p> + * Produces <code>Content-Type</code> types: <code>text/html</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * Essentially the same as {@link HtmlSerializer}, except serializes the POJO metamodel + * instead of the model itself. + * <p> + * Produces output that describes the POJO metamodel similar to an XML schema document. + * <p> + * The easiest way to create instances of this class is through the {@link HtmlSerializer#getSchemaSerializer()}, + * which will create a schema serializer with the same settings as the originating serializer. + * + * @author James Bognar ([email protected]) + */ +@Produces(value="text/html+schema", contentType="text/html") +public final class HtmlSchemaDocSerializer extends HtmlDocSerializer { + + /** + * Constructor. + */ + public HtmlSchemaDocSerializer() { + setProperty(SERIALIZER_detectRecursions, true); + setProperty(SERIALIZER_ignoreRecursions, true); + } + + @Override /* ISchemaSerializer */ + protected void doSerialize(Object o, Writer out, SerializerContext ctx) throws IOException, SerializeException { + HtmlSerializerContext hctx = (HtmlSerializerContext)ctx; + ObjectMap schema = getSchema(ctx.getBeanContext().getClassMetaForObject(o), hctx, "root", null); + super.doSerialize(schema, out, ctx); + } + + /* + * 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, HtmlSerializerContext ctx, String attrName, String[] pNames) throws SerializeException { + try { + + ObjectMap out = new ObjectMap(); + + ClassMeta<?> aType; // The actual type (will be null if recursion occurs) + ClassMeta<?> gType; // The generic type + + aType = ctx.push(attrName, eType, null); + + gType = eType.getFilteredClassMeta(); + String type = null; + + if (gType.isEnum() || gType.isCharSequence() || gType.isChar()) + type = "string"; + else if (gType.isNumber()) + type = "number"; + else if (gType.isBoolean()) + type = "boolean"; + else if (gType.isBean() || gType.isMap()) + type = "object"; + else if (gType.isCollection() || gType.isArray()) + type = "array"; + else + type = "any"; + + out.put("type", type); + out.put("class", eType.toString()); + PojoFilter f = eType.getPojoFilter(); + if (f != null) + out.put("filter", f); + + if (aType != null) { + if (gType.isEnum()) + out.put("enum", getEnumStrings((Class<Enum<?>>)gType.getInnerClass())); + else if (gType.isCollection() || gType.isArray()) { + ClassMeta componentType = gType.getElementType(); + if (gType.isCollection() && isParentClass(Set.class, gType.getInnerClass())) + out.put("uniqueItems", true); + out.put("items", getSchema(componentType, ctx, "items", pNames)); + } else if (gType.isBean()) { + ObjectMap properties = new ObjectMap(); + BeanMeta bm = ctx.getBeanContext().getBeanMeta(gType.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(), ctx, p.getName(), p.getProperties())); + } + out.put("properties", properties); + } + } + ctx.pop(); + return out; + } catch (StackOverflowError e) { + throw e; + } catch (Throwable e) { + throw new SerializeException("Exception occured trying to process object of type ''{0}''", eType).initCause(e); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private List<String> getEnumStrings(Class<? extends Enum> c) { + List<String> l = new LinkedList<String>(); + try { + for (Object e : EnumSet.allOf(c)) + l.add(e.toString()); + } catch (Exception e) { + e.printStackTrace(); + } + return l; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer$Sq.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer$Sq.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer$Sq.class new file mode 100755 index 0000000..dc9c801 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer$Sq.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer$SqReadable.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer$SqReadable.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer$SqReadable.class new file mode 100755 index 0000000..6e0df08 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer$SqReadable.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer.class new file mode 100755 index 0000000..e182a2c Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer.java new file mode 100755 index 0000000..022235d --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializer.java @@ -0,0 +1,668 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.html; + +import static com.ibm.juno.core.html.HtmlSerializerProperties.*; +import static com.ibm.juno.core.serializer.SerializerProperties.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.filter.*; +import com.ibm.juno.core.serializer.*; +import com.ibm.juno.core.xml.*; +import com.ibm.juno.core.xml.annotation.*; + +/** + * Serializes POJO models to HTML. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Accept</code> types: <code>text/html</code> + * <p> + * Produces <code>Content-Type</code> types: <code>text/html</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * The conversion is as follows... + * <ul> + * <li>{@link Map Maps} (e.g. {@link HashMap}, {@link TreeMap}) and beans are converted to HTML tables with 'key' and 'value' columns. + * <li>{@link Collection Collections} (e.g. {@link HashSet}, {@link LinkedList}) and Java arrays are converted to HTML ordered lists. + * <li>{@code Collections} of {@code Maps} and beans are converted to HTML tables with keys as headers. + * <li>Everything else is converted to text. + * </ul> + * <p> + * This serializer provides several serialization options. Typically, one of the predefined <jsf>DEFAULT</jsf> serializers will be sufficient. + * However, custom serializers can be constructed to fine-tune behavior. + * <p> + * The {@link HtmlLink} annotation can be used on beans to add hyperlinks to the output. + * + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link HtmlSerializerProperties} + * <li>{@link SerializerProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * + * <h6 class='topic'>Behavior-specific subclasses</h6> + * <p> + * The following direct subclasses are provided for convenience: + * <ul> + * <li>{@link Sq} - Default serializer, single quotes. + * <li>{@link SqReadable} - Default serializer, single quotes, whitespace added. + * </ul> + * + * + * <h6 class='topic'>Examples</h6> + * <p class='bcode'> + * <jc>// Use one of the default serializers to serialize a POJO</jc> + * String html = HtmlSerializer.<jsf>DEFAULT</jsf>.serialize(someObject); + * + * <jc>// Create a custom serializer that doesn't use whitespace and newlines</jc> + * HtmlSerializer serializer = <jk>new</jk> HtmlSerializer() + * .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>false</jk>); + * + * <jc>// Same as above, except uses cloning</jc> + * HtmlSerializer serializer = HtmlSerializer.<jsf>DEFAULT</jsf>.clone() + * .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>false</jk>); + * + * <jc>// Serialize POJOs to HTML</jc> + * + * <jc>// Produces: </jc> + * <jc>// <ul><li>1<li>2<li>3</ul></jc> + * List l = new ObjectList(1, 2, 3); + * String html = HtmlSerializer.<jsf>DEFAULT</jsf>.serialize(l); + * + * <jc>// Produces: </jc> + * <jc>// <table> </jc> + * <jc>// <tr><th>firstName</th><th>lastName</th></tr> </jc> + * <jc>// <tr><td>Bob</td><td>Costas</td></tr> </jc> + * <jc>// <tr><td>Billy</td><td>TheKid</td></tr> </jc> + * <jc>// <tr><td>Barney</td><td>Miller</td></tr> </jc> + * <jc>// </table> </jc> + * l = <jk>new</jk> ObjectList(); + * l.add(<jk>new</jk> ObjectMap(<js>"{firstName:'Bob',lastName:'Costas'}"</js>)); + * l.add(<jk>new</jk> ObjectMap(<js>"{firstName:'Billy',lastName:'TheKid'}"</js>)); + * l.add(<jk>new</jk> ObjectMap(<js>"{firstName:'Barney',lastName:'Miller'}"</js>)); + * String html = HtmlSerializer.<jsf>DEFAULT</jsf>.serialize(l); + * + * <jc>// Produces: </jc> + * <jc>// <table> </jc> + * <jc>// <tr><th>key</th><th>value</th></tr> </jc> + * <jc>// <tr><td>foo</td><td>bar</td></tr> </jc> + * <jc>// <tr><td>baz</td><td>123</td></tr> </jc> + * <jc>// </table> </jc> + * Map m = <jk>new</jk> ObjectMap(<js>"{foo:'bar',baz:123}"</js>); + * String html = HtmlSerializer.<jsf>DEFAULT</jsf>.serialize(m); + * + * <jc>// HTML elements can be nested arbitrarily deep</jc> + * <jc>// Produces: </jc> + * <jc>// <table> </jc> + * <jc>// <tr><th>key</th><th>value</th></tr> </jc> + * <jc>// <tr><td>foo</td><td>bar</td></tr> </jc> + * <jc>// <tr><td>baz</td><td>123</td></tr> </jc> + * <jc>// <tr><td>someNumbers</td><td><ul><li>1<li>2<li>3</ul></td></tr> </jc> + * <jc>// <tr><td>someSubMap</td><td> </jc> + * <jc>// <table> </jc> + * <jc>// <tr><th>key</th><th>value</th></tr> </jc> + * <jc>// <tr><td>a</td><td>b</td></tr> </jc> + * <jc>// </table> </jc> + * <jc>// </td></tr> </jc> + * <jc>// </table> </jc> + * Map m = <jk>new</jk> ObjectMap(<js>"{foo:'bar',baz:123}"</js>); + * m.put("someNumbers", new ObjectList(1, 2, 3)); + * m.put(<js>"someSubMap"</js>, new ObjectMap(<js>"{a:'b'}"</js>)); + * String html = HtmlSerializer.<jsf>DEFAULT</jsf>.serialize(m); + * </p> + * + * + * @author James Bognar ([email protected]) + */ +@Produces("text/html") +@SuppressWarnings("hiding") +public class HtmlSerializer extends XmlSerializer { + + /** Default serializer, all default settings. */ + public static final HtmlSerializer DEFAULT = new HtmlSerializer().lock(); + + /** Default serializer, single quotes. */ + public static final HtmlSerializer DEFAULT_SQ = new HtmlSerializer.Sq().lock(); + + /** Default serializer, single quotes, whitespace added. */ + public static final HtmlSerializer DEFAULT_SQ_READABLE = new HtmlSerializer.SqReadable().lock(); + + /** Default serializer, single quotes. */ + public static class Sq extends HtmlSerializer { + /** Constructor */ + public Sq() { + setProperty(SERIALIZER_quoteChar, '\''); + } + } + + /** Default serializer, single quotes, whitespace added. */ + public static class SqReadable extends Sq { + /** Constructor */ + public SqReadable() { + setProperty(SERIALIZER_useIndentation, true); + } + } + + + /** HTML serializer properties currently set on this serializer. */ + protected transient HtmlSerializerProperties hsp = new HtmlSerializerProperties(); + + /** + * Main serialization routine. + * + * @param o The object being serialized. + * @param w The writer to serialize to. + * @param ctx The serialization context object. + * + * @return The same writer passed in. + * @throws IOException If a problem occurred trying to send output to the writer. + */ + private HtmlSerializerWriter doSerialize(Object o, HtmlSerializerWriter w, HtmlSerializerContext ctx) throws SerializeException, IOException { + serializeAnything(w, o, null, ctx, null, ctx.getInitialDepth()-1, null); + return w; + } + + /** + * Serialize the specified object to the specified writer. + * + * @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 ctx The context object that lives for the duration of this serialization. + * @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. + * @throws IOException + * @throws SerializeException If a problem occurred trying to convert the output. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected void serializeAnything(HtmlSerializerWriter out, Object o, ClassMeta<?> eType, HtmlSerializerContext ctx, String name, int indent, BeanPropertyMeta pMeta) throws IOException, SerializeException { + + BeanContext bc = ctx.getBeanContext(); + ClassMeta<?> aType = null; // The actual type + ClassMeta<?> gType = object(); // The generic type + + if (eType == null) + eType = object(); + + aType = ctx.push(name, o, eType); + + // Handle recursion + if (aType == null) { + o = null; + aType = object(); + } + + ctx.indent += indent; + int i = ctx.indent; + + // Determine the type. + if (o == null || (aType.isChar() && ((Character)o).charValue() == 0)) + out.tag(i, "null").nl(); + else { + + gType = aType.getFilteredClassMeta(); + String classAttr = null; + if (ctx.isAddClassAttrs() && ! eType.equals(aType)) + classAttr = aType.toString(); + + // Filter if necessary + PojoFilter filter = aType.getPojoFilter(); + if (filter != null) { + o = filter.filter(o); + + // If the filter's getFilteredClass() method returns Object, we need to figure out + // the actual type now. + if (gType.isObject()) + gType = bc.getClassMetaForObject(o); + } + + HtmlClassMeta html = gType.getHtmlMeta(); + + if (html.isAsXml() || (pMeta != null && pMeta.getHtmlMeta().isAsXml())) + super.serializeAnything(out, o, null, ctx, null, null, false, XmlFormat.NORMAL, null); + else if (html.isAsPlainText() || (pMeta != null && pMeta.getHtmlMeta().isAsPlainText())) + out.write(o == null ? "null" : o.toString()); + else if (o == null || (gType.isChar() && ((Character)o).charValue() == 0)) + out.tag(i, "null").nl(); + else if (gType.hasToObjectMapMethod()) + serializeMap(out, gType.toObjectMap(o), eType, ctx, classAttr, pMeta); + else if (gType.isBean()) + serializeBeanMap(out, bc.forBean(o), ctx, classAttr, pMeta); + else if (gType.isNumber()) + out.sTag(i, "number").append(o).eTag("number").nl(); + else if (gType.isBoolean()) + out.sTag(i, "boolean").append(o).eTag("boolean").nl(); + else if (gType.isMap()) { + if (o instanceof BeanMap) + serializeBeanMap(out, (BeanMap)o, ctx, classAttr, pMeta); + else + serializeMap(out, (Map)o, eType, ctx, classAttr, pMeta); + } + else if (gType.isCollection()) { + if (classAttr != null) + serializeCollection(out, (Collection)o, gType, ctx, name, classAttr, pMeta); + else + serializeCollection(out, (Collection)o, eType, ctx, name, null, pMeta); + } + else if (gType.isArray()) { + if (classAttr != null) + serializeCollection(out, toList(gType.getInnerClass(), o), gType, ctx, name, classAttr, pMeta); + else + serializeCollection(out, toList(gType.getInnerClass(), o), eType, ctx, name, null, pMeta); + } + else if (gType.isUri() || (pMeta != null && (pMeta.isUri() || pMeta.isBeanUri()))) { + String label = null; + String at = ctx.getUriAnchorText(); + if (at != null) { + if (at.equals(LAST_TOKEN)) { + label = o.toString(); + if (label.indexOf('/') != -1) + label = label.substring(label.lastIndexOf('/')+1); + } else if (at.equals(PROPERTY_NAME)) { + label = (pMeta != null ? pMeta.getName() : null); + } else { + label = o.toString(); + } + } + if (label == null) + label = o.toString(); + out.oTag(i, "a").attrUri("href", o).append('>'); + if (at != null && at.equals(URI)) + out.appendUri(label); + else + out.append(label); + out.eTag("a").nl(); + } + else + out.sTag(i, "string").encodeText(o).eTag("string").nl(); + } + ctx.pop(); + ctx.indent -= indent; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void serializeMap(HtmlSerializerWriter out, Map m, ClassMeta<?> type, HtmlSerializerContext ctx, String classAttr, BeanPropertyMeta<?> ppMeta) throws IOException, SerializeException { + ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType(); + + int i = ctx.getIndent(); + out.oTag(i, "table").attr("type", "object"); + if (classAttr != null) + out.attr("class", classAttr); + out.appendln(">"); + if (! (ppMeta != null && ppMeta.getHtmlMeta().isNoTableHeaders())) { + out.sTag(i+1, "tr").nl(); + out.sTag(i+2, "th").nl().appendln(i+3, "<string>key</string>").eTag(i+2, "th").nl(); + out.sTag(i+2, "th").nl().appendln(i+3, "<string>value</string>").eTag(i+2, "th").nl(); + out.eTag(i+1, "tr").nl(); + } + for (Map.Entry e : (Set<Map.Entry>)m.entrySet()) { + + Object key = generalize(ctx, e.getKey(), keyType); + Object value = null; + try { + value = e.getValue(); + } catch (StackOverflowError t) { + throw t; + } catch (Throwable t) { + ctx.addWarning("Could not call getValue() on property ''{0}'', {1}", e.getKey(), t.getLocalizedMessage()); + } + + out.sTag(i+1, "tr").nl(); + out.sTag(i+2, "td").nl(); + serializeAnything(out, key, keyType, ctx, null, 2, null); + out.eTag(i+2, "td").nl(); + out.sTag(i+2, "td").nl(); + serializeAnything(out, value, valueType, ctx, (key == null ? "_x0000_" : key.toString()), 2, null); + out.eTag(i+2, "td").nl(); + out.eTag(i+1, "tr").nl(); + } + out.eTag(i, "table").nl(); + } + + @SuppressWarnings({ "rawtypes" }) + private void serializeBeanMap(HtmlSerializerWriter out, BeanMap m, HtmlSerializerContext ctx, String classAttr, BeanPropertyMeta<?> ppMeta) throws IOException, SerializeException { + int i = ctx.getIndent(); + + Object o = m.getBean(); + + 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(i, "a").attrUri("href", urlProp).append('>').encodeText(nameProp).eTag("a").nl(); + return; + } + + out.oTag(i, "table").attr("type", "object"); + if (classAttr != null) + out.attr("_class", classAttr); + out.append('>').nl(); + if (! (m.getClassMeta().getHtmlMeta().isNoTableHeaders() || (ppMeta != null && ppMeta.getHtmlMeta().isNoTableHeaders()))) { + out.sTag(i+1, "tr").nl(); + out.sTag(i+2, "th").nl().appendln(i+3, "<string>key</string>").eTag(i+2, "th").nl(); + out.sTag(i+2, "th").nl().appendln(i+3, "<string>value</string>").eTag(i+2, "th").nl(); + out.eTag(i+1, "tr").nl(); + } + + Iterator mapEntries = m.entrySet().iterator(); + + while (mapEntries.hasNext()) { + BeanMapEntry p = (BeanMapEntry)mapEntries.next(); + BeanPropertyMeta pMeta = p.getMeta(); + + String key = p.getKey(); + Object value = null; + try { + value = p.getValue(); + } catch (StackOverflowError e) { + throw e; + } catch (Throwable t) { + ctx.addBeanGetterWarning(pMeta, t); + } + + if (canIgnoreValue(ctx, pMeta.getClassMeta(), key, value)) + continue; + + out.sTag(i+1, "tr").nl(); + out.sTag(i+2, "td").nl(); + out.sTag(i+3, "string").encodeText(key).eTag("string").nl(); + out.eTag(i+2, "td").nl(); + out.sTag(i+2, "td").nl(); + try { + serializeAnything(out, value, p.getMeta().getClassMeta(), ctx, key, 2, pMeta); + } catch (SerializeException t) { + throw t; + } catch (StackOverflowError t) { + throw t; + } catch (Throwable t) { + ctx.addBeanGetterWarning(pMeta, t); + } + out.eTag(i+2, "td").nl(); + out.eTag(i+1, "tr").nl(); + } + out.eTag(i, "table").nl(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void serializeCollection(HtmlSerializerWriter out, Collection c, ClassMeta<?> type, HtmlSerializerContext ctx, String name, String classAttr, BeanPropertyMeta<?> ppMeta) throws IOException, SerializeException { + + BeanContext bc = ctx.getBeanContext(); + ClassMeta<?> elementType = type.getElementType(); + + int i = ctx.getIndent(); + if (c.isEmpty()) { + out.appendln(i, "<ul></ul>"); + return; + } + + c = sort(ctx, c); + + // 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. + String[] th = getTableHeaders(ctx, c, ppMeta); + + if (th != null) { + + out.oTag(i, "table").attr("type", "array"); + if (classAttr != null) + out.attr("_class", classAttr); + out.append('>').nl(); + out.sTag(i+1, "tr").nl(); + for (String key : th) + out.sTag(i+2, "th").append(key).eTag("th").nl(); + out.eTag(i+1, "tr").nl(); + + for (Object o : c) { + ClassMeta<?> cm = bc.getClassMetaForObject(o); + + if (cm != null && cm.getPojoFilter() != null) { + PojoFilter f = cm.getPojoFilter(); + o = f.filter(o); + cm = cm.getFilteredClassMeta(); + } + + if (cm != null && ctx.isAddClassAttrs() && elementType.getInnerClass() != o.getClass()) + out.oTag(i+1, "tr").attr("_class", o.getClass().getName()).append('>').nl(); + else + out.sTag(i+1, "tr").nl(); + + if (cm == null) { + serializeAnything(out, o, null, ctx, null, 1, null); + + } else if (cm.isMap() && ! (cm.isBeanMap())) { + Map m2 = sort(ctx, (Map)o); + + Iterator mapEntries = m2.entrySet().iterator(); + while (mapEntries.hasNext()) { + Map.Entry e = (Map.Entry)mapEntries.next(); + out.sTag(i+2, "td").nl(); + serializeAnything(out, e.getValue(), elementType, ctx, e.getKey().toString(), 2, null); + out.eTag(i+2, "td").nl(); + } + } else { + BeanMap m2 = null; + if (o instanceof BeanMap) + m2 = (BeanMap)o; + else + m2 = bc.forBean(o); + + Iterator mapEntries = m2.entrySet().iterator(); + while (mapEntries.hasNext()) { + BeanMapEntry p = (BeanMapEntry)mapEntries.next(); + BeanPropertyMeta pMeta = p.getMeta(); + out.sTag(i+2, "td").nl(); + serializeAnything(out, p.getValue(), pMeta.getClassMeta(), ctx, p.getKey().toString(), 2, pMeta); + out.eTag(i+2, "td").nl(); + } + } + out.eTag(i+1, "tr").nl(); + } + out.eTag(i, "table").nl(); + + } else { + out.sTag(i, "ul").nl(); + for (Object o : c) { + out.sTag(i+1, "li").nl(); + serializeAnything(out, o, elementType, ctx, name, 1, null); + out.eTag(i+1, "li").nl(); + } + out.eTag(i, "ul").nl(); + } + } + + /* + * 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 String[] getTableHeaders(SerializerContext ctx, Collection c, BeanPropertyMeta<?> pMeta) throws SerializeException { + BeanContext bc = ctx.getBeanContext(); + if (c.size() == 0) + return null; + c = sort(ctx, c); + String[] th; + Set<String> s = new TreeSet<String>(); + 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 = bc.getClassMetaForObject(o1); + if (cm.getPojoFilter() != null) { + PojoFilter f = cm.getPojoFilter(); + o1 = f.filter(o1); + cm = cm.getFilteredClassMeta(); + } + if (cm == null || ! (cm.isMap() || cm.isBean())) + return null; + if (cm.getInnerClass().isAnnotationPresent(HtmlLink.class)) + return null; + HtmlClassMeta h = cm.getHtmlMeta(); + if (h.isNoTables() || (pMeta != null && pMeta.getHtmlMeta().isNoTables())) + return null; + if (h.isNoTableHeaders() || (pMeta != null && pMeta.getHtmlMeta().isNoTableHeaders())) + return new String[0]; + if (canIgnoreValue(ctx, cm, null, o1)) + return null; + if (cm.isMap() && ! cm.isBeanMap()) { + Map m = (Map)o1; + th = new String[m.size()]; + int i = 0; + for (Object k : m.keySet()) + th[i++] = (k == null ? null : k.toString()); + } else { + BeanMap<?> bm = (o1 instanceof BeanMap ? (BeanMap)o1 : bc.forBean(o1)); + List<String> l = new LinkedList<String>(); + for (String k : bm.keySet()) + l.add(k); + th = l.toArray(new String[l.size()]); + } + prevC.add(cm); + s.addAll(Arrays.asList(th)); + + for (Object o : c) { + if (o == null) + continue; + cm = bc.getClassMetaForObject(o); + if (cm != null && cm.getPojoFilter() != null) { + PojoFilter f = cm.getPojoFilter(); + o = f.filter(o); + cm = cm.getFilteredClassMeta(); + } + if (prevC.contains(cm)) + continue; + if (cm == null || ! (cm.isMap() || cm.isBean())) + return null; + if (cm.getInnerClass().isAnnotationPresent(HtmlLink.class)) + return null; + if (canIgnoreValue(ctx, 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 : bc.forBean(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() { + HtmlSchemaDocSerializer s = new HtmlSchemaDocSerializer(); + s.beanContextFactory = this.beanContextFactory; + s.sp = this.sp; + s.xsp = this.xsp; + s.hsp = this.hsp; + return s; + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Serializer */ + public HtmlSerializerContext createContext(ObjectMap properties, Method javaMethod) { + return new HtmlSerializerContext(getBeanContext(), sp, xsp, hsp, properties, javaMethod); + } + + @Override /* Serializer */ + protected void doSerialize(Object o, Writer out, SerializerContext ctx) throws IOException, SerializeException { + HtmlSerializerContext hctx = (HtmlSerializerContext)ctx; + doSerialize(o, hctx.getWriter(out), hctx); + } + + @Override /* CoreApi */ + public HtmlSerializer setProperty(String property, Object value) throws LockedException { + if (! hsp.setProperty(property, value)) + super.setProperty(property, value); + return this; + } + + @Override /* CoreApi */ + public HtmlSerializer setProperties(ObjectMap properties) throws LockedException { + for (Map.Entry<String,Object> e : properties.entrySet()) + setProperty(e.getKey(), e.getValue()); + return this; + } + + @Override /* CoreApi */ + public HtmlSerializer addNotBeanClasses(Class<?>...classes) throws LockedException { + super.addNotBeanClasses(classes); + return this; + } + + @Override /* CoreApi */ + public HtmlSerializer addFilters(Class<?>...classes) throws LockedException { + super.addFilters(classes); + return this; + } + + @Override /* CoreApi */ + public <T> HtmlSerializer addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + super.addImplClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreApi */ + public HtmlSerializer setClassLoader(ClassLoader classLoader) throws LockedException { + super.setClassLoader(classLoader); + return this; + } + + @Override /* Lockable */ + public HtmlSerializer lock() { + super.lock(); + return this; + } + + @Override /* Lockable */ + public HtmlSerializer clone() { + HtmlSerializer c = (HtmlSerializer)super.clone(); + c.hsp = hsp.clone(); + return c; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerContext.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerContext.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerContext.class new file mode 100755 index 0000000..d765a69 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerContext.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerContext.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerContext.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerContext.java new file mode 100755 index 0000000..16c8774 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerContext.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.html; + +import static com.ibm.juno.core.html.HtmlDocSerializerProperties.*; +import static com.ibm.juno.core.html.HtmlSerializerProperties.*; + +import java.io.*; +import java.lang.reflect.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.serializer.*; +import com.ibm.juno.core.utils.*; +import com.ibm.juno.core.xml.*; + +/** + * Context object that lives for the duration of a single serialization of {@link HtmlSerializer} and its subclasses. + * <p> + * See {@link SerializerContext} for details. + * + * @author James Bognar ([email protected]) + */ +public final class HtmlSerializerContext extends XmlSerializerContext { + + private final String uriAnchorText, title, description, cssUrl; + private final String[] cssImports; + private final ObjectMap links; + private final boolean nowrap; + + /** + * Constructor. + * + * @param beanContext The bean context being used by the serializer. + * @param sp Default general serializer properties. + * @param xsp Default XML serializer properties. + * @param hsp Default HTML serializer properties. + * @param op Override properties. + * @param javaMethod Java method that invoked this serializer. + * When using the REST API, this is the Java method invoked by the REST call. + * Can be used to access annotations defined on the method or class. + */ + protected HtmlSerializerContext(BeanContext beanContext, SerializerProperties sp, XmlSerializerProperties xsp, HtmlSerializerProperties hsp, ObjectMap op, Method javaMethod) { + super(beanContext, sp, xsp, op, javaMethod); + if (op == null || op.isEmpty()) { + uriAnchorText = hsp.uriAnchorText; + title = hsp.title; + description = hsp.description; + links = hsp.links; + cssUrl = hsp.cssUrl; + cssImports = hsp.cssImports; + nowrap = hsp.nowrap; + } else { + uriAnchorText = op.getString(HTML_uriAnchorText, hsp.uriAnchorText); + title = op.getString(HTMLDOC_title, hsp.title); + description = op.getString(HTMLDOC_description, hsp.description); + ObjectMap m = op.getObjectMap(HTMLDOC_links, hsp.links); + if (op.containsKey(HTMLDOC_addLinks)) + if (m == null) + m = op.getObjectMap(HTMLDOC_addLinks, null); + else + m.putAll(op.getObjectMap(HTMLDOC_addLinks, null)); + links = m; + cssUrl = op.getString(HTMLDOC_cssUrl, hsp.cssUrl); + cssImports = StringUtils.split(op.getString(HTMLDOC_cssImports, null), ','); + nowrap = op.getBoolean(HTMLDOC_cssUrl, hsp.nowrap); + } + } + + /** + * Returns the {@link HtmlSerializerProperties#HTML_uriAnchorText} setting value in this context. + * + * @return The {@link HtmlSerializerProperties#HTML_uriAnchorText} setting value in this context. + */ + public final String getUriAnchorText() { + return uriAnchorText; + } + + /** + * Returns the {@link HtmlDocSerializerProperties#HTMLDOC_title} setting value in this context. + * + * @return The {@link HtmlDocSerializerProperties#HTMLDOC_title} setting value in this context. + */ + public final String getTitle() { + return title; + } + + /** + * Returns the {@link HtmlDocSerializerProperties#HTMLDOC_description} setting value in this context. + * + * @return The {@link HtmlDocSerializerProperties#HTMLDOC_description} setting value in this context. + */ + public final String getDescription() { + return description; + } + + /** + * Returns the {@link HtmlDocSerializerProperties#HTMLDOC_links} setting value in this context. + * + * @return The {@link HtmlDocSerializerProperties#HTMLDOC_links} setting value in this context. + */ + public final ObjectMap getLinks() { + return links; + } + + /** + * Returns the {@link HtmlDocSerializerProperties#HTMLDOC_cssUrl} setting value in this context. + * + * @return The {@link HtmlDocSerializerProperties#HTMLDOC_cssUrl} setting value in this context. + */ + public final String getCssUrl() { + return cssUrl; + } + + /** + * Returns the {@link HtmlDocSerializerProperties#HTMLDOC_cssImports} setting value in this context. + * + * @return The {@link HtmlDocSerializerProperties#HTMLDOC_cssImports} setting value in this context. + */ + public final String[] getCssImports() { + return cssImports; + } + + /** + * Returns the {@link HtmlDocSerializerProperties#HTMLDOC_nowrap} setting value in this context. + * + * @return The {@link HtmlDocSerializerProperties#HTMLDOC_nowrap} setting value in this context. + */ + public final boolean isNoWrap() { + return nowrap; + } + + /** + * Wraps the specified writer in a {@link HtmlSerializerWriter}. + */ + @Override /* XmlSerializerContext */ + public HtmlSerializerWriter getWriter(Writer w) { + if (w instanceof HtmlSerializerWriter) + return (HtmlSerializerWriter)w; + return new HtmlSerializerWriter(w, isUseIndentation(), getQuoteChar(), getRelativeUriBase(), getAbsolutePathUriBase()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerProperties.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerProperties.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerProperties.class new file mode 100755 index 0000000..6857e05 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerProperties.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerProperties.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerProperties.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerProperties.java new file mode 100755 index 0000000..8b6068d --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerProperties.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.html; + +import static com.ibm.juno.core.html.HtmlDocSerializerProperties.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.parser.*; +import com.ibm.juno.core.serializer.*; +import com.ibm.juno.core.utils.*; +import com.ibm.juno.core.xml.*; + +/** + * Configurable properties on the {@link HtmlSerializer} class. + * <p> + * Use the {@link HtmlSerializer#setProperty(String, Object)} method to set property values. + * <p> + * In addition to these properties, the following properties are also applicable for {@link HtmlSerializer}. + * <ul> + * <li>{@link XmlSerializerProperties} + * <li>{@link SerializerProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * @author James Bognar ([email protected]) + */ +public final class HtmlSerializerProperties implements Cloneable { + + /** + * Anchor text source ({@link String}, default={@link #TO_STRING}). + * <p> + * When creating anchor tags (e.g. <code><xt><a</xt> <xa>href</xa>=<xs>'...'</xs><xt>></xt>text<xt></a></xt></code>) + * in HTML, this setting defines what to set the inner text to. + * <p> + * Possible values: + * <ul> + * <li>{@link #TO_STRING} / <js>"toString"</js> - Set to whatever is returned by {@link #toString()} on the object. + * <li>{@link #URI} / <js>"uri"</js> - Set to the URI value. + * <li>{@link #LAST_TOKEN} / <js>"lastToken"</js> - Set to the last token of the URI value. + * <li>{@link #PROPERTY_NAME} / <js>"propertyName"</js> - Set to the bean property name. + * </ul> + */ + public static final String HTML_uriAnchorText = "HtmlSerializer.uriAnchorText"; + + /** Constant for {@link HtmlSerializerProperties#HTML_uriAnchorText} property. */ + public static final String PROPERTY_NAME = "propertyName"; + /** Constant for {@link HtmlSerializerProperties#HTML_uriAnchorText} property. */ + public static final String TO_STRING = "toString"; + /** Constant for {@link HtmlSerializerProperties#HTML_uriAnchorText} property. */ + public static final String URI = "uri"; + /** Constant for {@link HtmlSerializerProperties#HTML_uriAnchorText} property. */ + public static final String LAST_TOKEN = "lastToken"; + + String uriAnchorText = TO_STRING, title, description, cssUrl; + String[] cssImports; + ObjectMap links; + boolean nowrap; + + /** + * Sets the specified property value. + * + * @param property The property name. + * @param value The property value. + * @return <jk>true</jk> if property name was valid and property was set. + */ + public boolean setProperty(String property, Object value) { + if (property.equals(HTML_uriAnchorText)) + uriAnchorText = (value == null ? null : value.toString()); + else if (property.equals(HTMLDOC_title)) + title = (value == null ? null : value.toString()); + else if (property.equals(HTMLDOC_description)) + description = (value == null ? null : value.toString()); + else if (property.equals(HTMLDOC_nowrap)) + nowrap = Boolean.valueOf(value.toString()); + else if (property.equals(HTMLDOC_links)) + try { + links = new ObjectMap(value.toString()); + } catch (ParseException e) { + e.printStackTrace(); + } + else if (property.equals(HTMLDOC_addLinks)) + try { + if (links == null) + links = new ObjectMap(value.toString()); + else + links.putAll(new ObjectMap(value.toString())); + } catch (ParseException e) { + e.printStackTrace(); + } + else if (property.equals(HTMLDOC_cssUrl)) + cssUrl = (value == null ? null : value.toString()); + else if (property.equals(HTMLDOC_cssImports)) + cssImports = StringUtils.split(value == null ? null : value.toString(), ','); + else + return false; + return true; + } + + @Override /* Cloneable */ + public HtmlSerializerProperties clone() { + try { + return (HtmlSerializerProperties)super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Shouldn't happen + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerWriter.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerWriter.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerWriter.class new file mode 100755 index 0000000..52d07fe Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerWriter.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerWriter.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerWriter.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerWriter.java new file mode 100755 index 0000000..28e2eb4 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlSerializerWriter.java @@ -0,0 +1,328 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.html; + +import java.io.*; + +import com.ibm.juno.core.xml.*; + +/** + * Specialized writer for serializing HTML. + * + * @author James Bognar ([email protected]) + */ +public class HtmlSerializerWriter extends XmlSerializerWriter { + + /** + * Constructor. + * + * @param out The writer being wrapped. + * @param useIndentation If <jk>true</jk>, tabs will be used in output. + * @param quoteChar The quote character to use (i.e. <js>'\''</js> or <js>'"'</js>) + * @param uriContext The web application context path (e.g. "/contextRoot"). + * @param uriAuthority The web application URI authority (e.g. "http://hostname:9080") + */ + public HtmlSerializerWriter(Writer out, boolean useIndentation, char quoteChar, String uriContext, String uriAuthority) { + super(out, useIndentation, quoteChar, uriContext, uriAuthority, false, null); + } + + /** + * Append an attribute with a URI value. + * + * @param name The attribute name. + * @param value The attribute value. Can be any object whose <code>toString()</code> method returns a URI. + * @return This object (for method chaining); + * @throws IOException If a problem occurred. + */ + public HtmlSerializerWriter attrUri(String name, Object value) throws IOException { + super.attrUri((String)null, name, value); + return this; + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter encodeText(Object o) throws IOException { + + String s = o.toString(); + for (int i = 0; i < s.length(); i++) { + char test = s.charAt(i); + if (test == '&') + append("&"); + else if (test == '<') + append("<"); + else if (test == '>') + append(">"); + else if (test == '\n') + append("<br/>"); + else if (test == '\f') + append("<ff/>"); + else if (test == '\b') + append("<bs/>"); + else if (test == '\t') + append("<tb/>"); + else if (Character.isISOControl(test)) + append("&#" + (int) test + ";"); + else + append(test); + } + + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter oTag(String ns, String name, boolean needsEncoding) throws IOException { + super.oTag(ns, name, needsEncoding); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter oTag(String ns, String name) throws IOException { + super.oTag(ns, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter oTag(String name) throws IOException { + super.oTag(name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter oTag(int indent, String ns, String name, boolean needsEncoding) throws IOException { + super.oTag(indent, ns, name, needsEncoding); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter oTag(int indent, String ns, String name) throws IOException { + super.oTag(indent, ns, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter oTag(int indent, String name) throws IOException { + super.oTag(indent, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter tag(String ns, String name, boolean needsEncoding) throws IOException { + super.tag(ns, name, needsEncoding); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter tag(String ns, String name) throws IOException { + super.tag(ns, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter tag(String name) throws IOException { + super.tag(name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter tag(int indent, String name) throws IOException { + super.tag(indent, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter tag(int indent, String ns, String name, boolean needsEncoding) throws IOException { + super.tag(indent, ns, name, needsEncoding); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter tag(int indent, String ns, String name) throws IOException { + super.tag(indent, ns, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter sTag(String ns, String name) throws IOException { + super.sTag(ns, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter sTag(String ns, String name, boolean needsEncoding) throws IOException { + super.sTag(ns, name, needsEncoding); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter sTag(int indent, String ns, String name) throws IOException { + super.sTag(indent, ns, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter sTag(int indent, String name) throws IOException { + super.sTag(indent, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter sTag(String name) throws IOException { + super.sTag(name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter sTag(int indent, String ns, String name, boolean needsEncoding) throws IOException { + super.sTag(indent, ns, name, needsEncoding); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter eTag(String ns, String name) throws IOException { + super.eTag(ns, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter eTag(String ns, String name, boolean needsEncoding) throws IOException { + super.eTag(ns, name, needsEncoding); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter eTag(int indent, String ns, String name) throws IOException { + super.eTag(indent, ns, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter eTag(int indent, String name) throws IOException { + super.eTag(indent, name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter eTag(String name) throws IOException { + super.eTag(name); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter eTag(int indent, String ns, String name, boolean needsEncoding) throws IOException { + super.eTag(indent, ns, name, needsEncoding); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter attr(String name, Object value) throws IOException { + super.attr(name, value); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter attr(String ns, String name, Object value) throws IOException { + super.attr(ns, name, value); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter attr(String ns, String name, Object value, boolean needsEncoding) throws IOException { + super.attr(ns, name, value, needsEncoding); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter attr(String name, Object value, boolean needsEncoding) throws IOException { + super.attr(null, name, value, needsEncoding); + return this; + } + + @Override /* XmlSerializerWriter */ + public HtmlSerializerWriter oAttr(String ns, String name) throws IOException { + super.oAttr(ns, name); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter cr(int depth) throws IOException { + super.cr(depth); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter appendln(int indent, String text) throws IOException { + super.appendln(indent, text); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter appendln(String text) throws IOException { + super.appendln(text); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter append(int indent, String text) throws IOException { + super.append(indent, text); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter append(int indent, char c) throws IOException { + super.append(indent, c); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter s() throws IOException { + super.s(); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter q() throws IOException { + super.q(); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter i(int indent) throws IOException { + super.i(indent); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter nl() throws IOException { + super.nl(); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter append(Object text) throws IOException { + super.append(text); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter append(String text) throws IOException { + super.append(text); + return this; + } + + @Override /* SerializerWriter */ + public HtmlSerializerWriter append(char c) throws IOException { + super.append(c); + return this; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlStrippedDocSerializer.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlStrippedDocSerializer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlStrippedDocSerializer.class new file mode 100755 index 0000000..5e453b7 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlStrippedDocSerializer.class differ
