http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java index f632c84..4a8177d 100644 --- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java @@ -14,20 +14,20 @@ package org.apache.juneau.msgpack; import static org.apache.juneau.msgpack.MsgPackSerializerContext.*; -import java.lang.reflect.*; import java.util.*; import org.apache.juneau.*; -import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; +import org.apache.juneau.transform.*; /** * Session object that lives for the duration of a single use of {@link MsgPackSerializer}. * * <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 within the same thread. */ -public final class MsgPackSerializerSession extends SerializerSession { +public final class MsgPackSerializerSession extends OutputStreamSerializerSession { private final boolean addBeanTypeProperties; @@ -38,28 +38,20 @@ public final class MsgPackSerializerSession extends SerializerSession { * @param ctx * The context creating this session object. * The context contains all the configuration settings for this object. - * @param op - * The override properties. - * These override any context properties defined in the context. - * @param javaMethod The java method that called this serializer, usually the method in a REST servlet. - * @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 uriContext - * The URI context. - * Identifies the current request URI used for resolution of URIs to absolute or root-relative form. + * @param args + * Runtime arguments. + * These specify session-level information such as locale and URI context. + * It also include session-level properties that override the properties defined on the bean and + * serializer contexts. + * <br>If <jk>null</jk>, defaults to {@link SerializerSessionArgs#DEFAULT}. */ - protected MsgPackSerializerSession(MsgPackSerializerContext ctx, ObjectMap op, Method javaMethod, - Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) { - super(ctx, op, javaMethod, locale, timeZone, mediaType, uriContext); - if (op == null || op.isEmpty()) { + protected MsgPackSerializerSession(MsgPackSerializerContext ctx, SerializerSessionArgs args) { + super(ctx, args); + ObjectMap p = getProperties(); + if (p.isEmpty()) { addBeanTypeProperties = ctx.addBeanTypeProperties; } else { - addBeanTypeProperties = op.getBoolean(MSGPACK_addBeanTypeProperties, ctx.addBeanTypeProperties); + addBeanTypeProperties = p.getBoolean(MSGPACK_addBeanTypeProperties, ctx.addBeanTypeProperties); } } @@ -69,22 +61,167 @@ public final class MsgPackSerializerSession extends SerializerSession { * @return The {@link MsgPackSerializerContext#MSGPACK_addBeanTypeProperties} setting value for this session. */ @Override /* SerializerSession */ - public final boolean isAddBeanTypeProperties() { + protected final boolean isAddBeanTypeProperties() { return addBeanTypeProperties; } - /** + @Override /* SerializerSession */ + protected void doSerialize(SerializerPipe out, Object o) throws Exception { + serializeAnything(getMsgPackOutputStream(out), o, getExpectedRootType(o), "root", null); + } + + /* * Converts the specified output target object to an {@link MsgPackOutputStream}. - * - * @param out The output target object. - * @return The output target object wrapped in an {@link MsgPackOutputStream}. - * @throws Exception */ - @SuppressWarnings("static-method") - public MsgPackOutputStream getMsgPackOutputStream(SerializerOutput out) throws Exception { + private static final MsgPackOutputStream getMsgPackOutputStream(SerializerPipe out) throws Exception { Object output = out.getRawOutput(); if (output instanceof MsgPackOutputStream) return (MsgPackOutputStream)output; - return new MsgPackOutputStream(out.getOutputStream()); + MsgPackOutputStream os = new MsgPackOutputStream(out.getOutputStream()); + out.setOutputStream(os); + return os; + } + + /* + * Workhorse method. + * Determines the type of object, and then calls the appropriate type-specific serialization method. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + private MsgPackOutputStream serializeAnything(MsgPackOutputStream out, Object o, ClassMeta<?> eType, String attrName, BeanPropertyMeta pMeta) throws Exception { + + if (o == null) + return out.appendNull(); + + if (eType == null) + eType = object(); + + ClassMeta<?> aType; // The actual type + ClassMeta<?> sType; // The serialized type + + aType = push(attrName, o, eType); + boolean isRecursion = aType == null; + + // Handle recursion + if (aType == null) { + o = null; + aType = object(); + } + + sType = aType.getSerializedClassMeta(); + String typeName = getBeanTypeName(eType, aType, pMeta); + + // Swap if necessary + PojoSwap swap = aType.getPojoSwap(); + if (swap != null) { + o = swap.swap(this, o); + + // If the getSwapClass() method returns Object, we need to figure out + // the actual type now. + if (sType.isObject()) + sType = getClassMetaForObject(o); + } + + // '\0' characters are considered null. + if (o == null || (sType.isChar() && ((Character)o).charValue() == 0)) + out.appendNull(); + else if (sType.isBoolean()) + out.appendBoolean((Boolean)o); + else if (sType.isNumber()) + out.appendNumber((Number)o); + else if (sType.isBean()) + serializeBeanMap(out, toBeanMap(o), typeName); + else if (sType.isUri() || (pMeta != null && pMeta.isUri())) + out.appendString(resolveUri(o.toString())); + else if (sType.isMap()) { + if (o instanceof BeanMap) + serializeBeanMap(out, (BeanMap)o, typeName); + else + serializeMap(out, (Map)o, eType); + } + else if (sType.isCollection()) { + serializeCollection(out, (Collection) o, eType); + } + else if (sType.isArray()) { + serializeCollection(out, toList(sType.getInnerClass(), o), eType); + } else + out.appendString(toString(o)); + + if (! isRecursion) + pop(); + return out; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void serializeMap(MsgPackOutputStream out, Map m, ClassMeta<?> type) throws Exception { + + ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType(); + + m = sort(m); + + // The map size may change as we're iterating over it, so + // grab a snapshot of the entries in a separate list. + List<SimpleMapEntry> entries = new ArrayList<SimpleMapEntry>(m.size()); + for (Map.Entry e : (Set<Map.Entry>)m.entrySet()) + entries.add(new SimpleMapEntry(e.getKey(), e.getValue())); + + out.startMap(entries.size()); + + for (SimpleMapEntry e : entries) { + Object value = e.value; + Object key = generalize(e.key, keyType); + + serializeAnything(out, key, keyType, null, null); + serializeAnything(out, value, valueType, null, null); + } + } + + private void serializeBeanMap(MsgPackOutputStream out, final BeanMap<?> m, String typeName) throws Exception { + + List<BeanPropertyValue> values = m.getValues(isTrimNulls(), typeName != null ? createBeanTypeNameProperty(m, typeName) : null); + + int size = values.size(); + for (BeanPropertyValue p : values) + if (p.getThrown() != null) + size--; + out.startMap(size); + + for (BeanPropertyValue p : values) { + BeanPropertyMeta pMeta = p.getMeta(); + ClassMeta<?> cMeta = p.getClassMeta(); + String key = p.getName(); + Object value = p.getValue(); + Throwable t = p.getThrown(); + if (t != null) + onBeanGetterException(pMeta, t); + else { + serializeAnything(out, key, null, null, null); + serializeAnything(out, value, cMeta, key, pMeta); + } + } + } + + private static class SimpleMapEntry { + final Object key; + final Object value; + + private SimpleMapEntry(Object key, Object value) { + this.key = key; + this.value = value; + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private void serializeCollection(MsgPackOutputStream out, Collection c, ClassMeta<?> type) throws Exception { + + ClassMeta<?> elementType = type.getElementType(); + List<Object> l = new ArrayList<Object>(c.size()); + + c = sort(c); + l.addAll(c); + + out.startArray(l.size()); + + for (Object o : l) + serializeAnything(out, o, elementType, "<iterator>", null); } }
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParser.java b/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParser.java index 46b1678..9fb15d6 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParser.java @@ -45,7 +45,7 @@ public abstract class InputStreamParser extends Parser { } @Override /* Parser */ - public boolean isReaderParser() { + public final boolean isReaderParser() { return false; } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParserSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParserSession.java b/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParserSession.java new file mode 100644 index 0000000..3d061a5 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParserSession.java @@ -0,0 +1,50 @@ +// *************************************************************************************************************************** +// * 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.parser; + +/** + * Subclass of parser session objects for byte-based parsers. + * + * <p> + * This class is NOT thread safe. It is typically discarded after one-time use. + */ +public abstract class InputStreamParserSession extends ParserSession { + + /** + * 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 session arguments. + */ + protected InputStreamParserSession(ParserContext ctx, ParserSessionArgs args) { + super(ctx, args); + } + + /** + * Constructor for sessions that don't require context. + * + * @param args + * Runtime session arguments. + */ + protected InputStreamParserSession(ParserSessionArgs args) { + this(null, args); + } + + @Override /* ParserSession */ + public final boolean isReaderParser() { + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ParseException.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/ParseException.java b/juneau-core/src/main/java/org/apache/juneau/parser/ParseException.java index f675377..f90d497 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/ParseException.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParseException.java @@ -15,10 +15,8 @@ package org.apache.juneau.parser; import static org.apache.juneau.internal.StringUtils.*; import java.text.*; -import java.util.*; import org.apache.juneau.*; -import org.apache.juneau.json.*; /** * Exception that indicates invalid syntax encountered during parsing. @@ -30,12 +28,12 @@ public class ParseException extends FormattedException { /** * Constructor. * - * @param session The parser session to extract information from. + * @param location The location of the parse exception. * @param message The exception message containing {@link MessageFormat}-style arguments. * @param args Optional {@link MessageFormat}-style arguments. */ - public ParseException(ParserSession session, String message, Object...args) { - super(getMessage(session, message, args)); + public ParseException(ObjectMap location, String message, Object...args) { + super(getMessage(location, message, args)); } /** @@ -51,11 +49,11 @@ public class ParseException extends FormattedException { /** * Constructor. * - * @param session The parser session to extract information from. + * @param location The location of the parse exception. * @param causedBy The inner exception. */ - public ParseException(ParserSession session, Exception causedBy) { - super(causedBy, getMessage(session, causedBy.getMessage())); + public ParseException(ObjectMap location, Exception causedBy) { + super(causedBy, getMessage(location, causedBy.getMessage())); } /** @@ -67,13 +65,11 @@ public class ParseException extends FormattedException { super(causedBy, getMessage(null, causedBy.getMessage())); } - private static String getMessage(ParserSession session, String msg, Object... args) { + private static String getMessage(ObjectMap location, String msg, Object... args) { if (args.length != 0) msg = format(msg, args); - if (session != null) { - Map<String,Object> m = session.getLastLocation(); - if (m != null && ! m.isEmpty()) - msg = "Parse exception occurred at " + JsonSerializer.DEFAULT_LAX.toString(m) + ". " + msg; + if (location != null && ! location.isEmpty()) { + msg = "Parse exception occurred at " + location.toString() + ". " + msg; } return msg; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java b/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java index c28dbf9..b077654 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java @@ -145,12 +145,10 @@ public abstract class Parser extends CoreObject { /** General parser properties currently set on this parser. */ private final MediaType[] mediaTypes; - private final ParserContext ctx; // Hidden constructor to force subclass from InputStreamParser or ReaderParser. Parser(PropertyStore propertyStore) { super(propertyStore); - this.ctx = createContext(ParserContext.class); Consumes c = getAnnotation(Consumes.class, getClass()); if (c == null) @@ -168,74 +166,35 @@ public abstract class Parser extends CoreObject { return new ParserBuilder(propertyStore); } + //-------------------------------------------------------------------------------- // Abstract methods //-------------------------------------------------------------------------------- /** - * Workhorse method. Subclasses are expected to implement this method. - * - * @param session - * The runtime session object returned by {@link #createSession(Object, ObjectMap, Method, Object, - * Locale, TimeZone, MediaType)}. - * If <jk>null</jk>, one will be created using {@link #createSession(Object)}. - * @param type - * The class type of the object to create. - * If <jk>null</jk> or <code>Object.<jk>class</jk></code>, object type is based on what's being parsed. - * For example, when parsing JSON text, it may return a <code>String</code>, <code>Number</code>, - * <code>ObjectMap</code>, etc... - * @param <T> The class type of the object to create. - * @return The parsed object. - * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed. - */ - protected abstract <T> T doParse(ParserSession session, ClassMeta<T> type) throws Exception; - - /** * Returns <jk>true</jk> if this parser subclasses from {@link ReaderParser}. * * @return <jk>true</jk> if this parser subclasses from {@link ReaderParser}. */ public abstract boolean isReaderParser(); - //-------------------------------------------------------------------------------- - // Other methods - //-------------------------------------------------------------------------------- - /** - * Entry point for all parsing calls. + * Create the session object that will be passed in to the parse method. * * <p> - * Calls the {@link #doParse(ParserSession, ClassMeta)} implementation class and catches/re-wraps any exceptions - * thrown. + * It's up to implementers to decide what the session object looks like, although typically it's going to be a + * subclass of {@link ParserSession}. * - * @param session - * The runtime session returned by {@link #createSession(Object, ObjectMap, Method, Object, Locale, - * TimeZone, MediaType)}. - * @param type The class type of the object to create. - * @param <T> The class type of the object to create. - * @return The parsed object. - * @throws ParseException - * If the input contains a syntax error or is malformed, or is not valid for the specified type. + * @param args + * Runtime arguments. + * @return The new session. */ - public final <T> T parseSession(ParserSession session, ClassMeta<T> type) throws ParseException { - try { - if (type.isVoid()) - return null; - return doParse(session, type); - } catch (ParseException e) { - throw e; - } catch (StackOverflowError e) { - throw new ParseException(session, "Depth too deep. Stack overflow occurred."); - } catch (IOException e) { - throw new ParseException(session, "I/O exception occurred. exception={0}, message={1}.", - e.getClass().getSimpleName(), e.getLocalizedMessage()).initCause(e); - } catch (Exception e) { - throw new ParseException(session, "Exception occurred. exception={0}, message={1}.", - e.getClass().getSimpleName(), e.getLocalizedMessage()).initCause(e); - } finally { - session.close(); - } - } + public abstract ParserSession createSession(ParserSessionArgs args); + + + //-------------------------------------------------------------------------------- + // Other methods + //-------------------------------------------------------------------------------- /** * Parses input into the specified object type. @@ -313,10 +272,13 @@ public abstract class Parser extends CoreObject { * If the input contains a syntax error or is malformed, or is not valid for the specified type. * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. */ - @SuppressWarnings("unchecked") public final <T> T parse(Object input, Type type, Type...args) throws ParseException { - ParserSession session = createSession(input); - return (T)parseSession(session, session.getClassMeta(type, args)); + ParserSession session = createSession(); + try { + return session.parse(input, type, args); + } finally { + session.close(); + } } /** @@ -355,8 +317,12 @@ public abstract class Parser extends CoreObject { * If the input contains a syntax error or is malformed, or is not valid for the specified type. */ public final <T> T parse(Object input, Class<T> type) throws ParseException { - ParserSession session = createSession(input); - return parseSession(session, session.getClassMeta(type)); + ParserSession session = createSession(); + try { + return session.parse(input, type); + } finally { + session.close(); + } } /** @@ -376,37 +342,12 @@ public abstract class Parser extends CoreObject { * If the input contains a syntax error or is malformed, or is not valid for the specified type. */ public final <T> T parse(Object input, ClassMeta<T> type) throws ParseException { - return parseSession(createSession(input), type); - } - - /** - * Create the session object that will be passed in to the parse method. - * - * <p> - * It's up to implementers to decide what the session object looks like, although typically it's going to be a - * subclass of {@link ParserSession}. - * - * @param input - * The input. - * See {@link #parse(Object, ClassMeta)} for supported input types. - * @param op Optional additional properties. - * @param javaMethod - * Java method that invoked this parser. - * 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. - * @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>). - * @return The new session. - */ - public ParserSession createSession(Object input, ObjectMap op, Method javaMethod, Object outer, Locale locale, - TimeZone timeZone, MediaType mediaType) { - return new ParserSession(ctx, op, input, javaMethod, outer, locale, timeZone, mediaType); + ParserSession session = createSession(); + try { + return session.parse(input, type); + } finally { + session.close(); + } } /** @@ -415,11 +356,10 @@ public abstract class Parser extends CoreObject { * <p> * Equivalent to calling <code>createSession(<jk>null</jk>, <jk>null</jk>)</code>. * - * @param input The input. See {@link #parse(Object, ClassMeta)} for supported input types. * @return The new context. */ - protected final ParserSession createSession(Object input) { - return createSession(input, null, null, null, null, null, getPrimaryMediaType()); + public final ParserSession createSession() { + return createSession(null); } @@ -452,39 +392,15 @@ public abstract class Parser extends CoreObject { * @throws UnsupportedOperationException If not implemented. */ public final <K,V> Map<K,V> parseIntoMap(Object input, Map<K,V> m, Type keyType, Type valueType) throws ParseException { - ParserSession session = createSession(input); + ParserSession session = createSession(); try { - return doParseIntoMap(session, m, keyType, valueType); - } catch (ParseException e) { - throw e; - } catch (Exception e) { - throw new ParseException(session, e); + return session.parseIntoMap(input, m, keyType, valueType); } finally { session.close(); } } /** - * Implementation method. - * - * <p> - * Default implementation throws an {@link UnsupportedOperationException}. - * - * @param session - * The runtime session object returned by - * {@link #createSession(Object, ObjectMap, Method, Object, Locale, TimeZone, MediaType)}. - * If <jk>null</jk>, one will be created using {@link #createSession(Object)}. - * @param m The map being loaded. - * @param keyType The class type of the keys, or <jk>null</jk> to default to <code>String.<jk>class</jk></code>. - * @param valueType The class type of the values, or <jk>null</jk> to default to whatever is being parsed. - * @return The same map that was passed in to allow this method to be chained. - * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed. - */ - protected <K,V> Map<K,V> doParseIntoMap(ParserSession session, Map<K,V> m, Type keyType, Type valueType) throws Exception { - throw new UnsupportedOperationException("Parser '"+getClass().getName()+"' does not support this method."); - } - - /** * Parses the contents of the specified reader and loads the results into the specified collection. * * <p> @@ -504,40 +420,16 @@ public abstract class Parser extends CoreObject { * If the input contains a syntax error or is malformed, or is not valid for the specified type. * @throws UnsupportedOperationException If not implemented. */ - public final <E> Collection<E> parseIntoCollection(Object input, Collection<E> c, Type elementType) - throws ParseException { - ParserSession session = createSession(input); + public final <E> Collection<E> parseIntoCollection(Object input, Collection<E> c, Type elementType) throws ParseException { + ParserSession session = createSession(); try { - return doParseIntoCollection(session, c, elementType); - } catch (ParseException e) { - throw e; - } catch (Exception e) { - throw new ParseException(session, e); + return session.parseIntoCollection(input, c, elementType); } finally { session.close(); } } /** - * Implementation method. - * - * <p> - * Default implementation throws an {@link UnsupportedOperationException}. - * - * @param session - * The runtime session object returned by {@link #createSession(Object, ObjectMap, Method, Object, - * Locale, TimeZone, MediaType)}. - * If <jk>null</jk>, one will be created using {@link #createSession(Object)}. - * @param c The collection being loaded. - * @param elementType The class type of the elements, or <jk>null</jk> to default to whatever is being parsed. - * @return The same collection that was passed in to allow this method to be chained. - * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed. - */ - protected <E> Collection<E> doParseIntoCollection(ParserSession session, Collection<E> c, Type elementType) throws Exception { - throw new UnsupportedOperationException("Parser '"+getClass().getName()+"' does not support this method."); - } - - /** * Parses the specified array input with each entry in the object defined by the {@code argTypes} * argument. * @@ -561,13 +453,9 @@ public abstract class Parser extends CoreObject { public final Object[] parseArgs(Object input, Type[] argTypes) throws ParseException { if (argTypes == null || argTypes.length == 0) return new Object[0]; - ParserSession session = createSession(input); + ParserSession session = createSession(); try { - return doParse(session, session.getArgsClassMeta(argTypes)); - } catch (ParseException e) { - throw e; - } catch (Exception e) { - throw new ParseException(session, e); + return session.parseArgs(input, argTypes); } finally { session.close(); } @@ -579,83 +467,6 @@ public abstract class Parser extends CoreObject { //-------------------------------------------------------------------------------- /** - * Converts the specified string to the specified type. - * - * @param session The session object. - * @param outer - * The outer object if we're converting to an inner object that needs to be created within the context - * of an outer object. - * @param s The string to convert. - * @param type The class type to convert the string to. - * @return The string converted as an object of the specified type. - * @throws Exception If the input contains a syntax error or is malformed, or is not valid for the specified type. - * @param <T> The class type to convert the string to. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected <T> T convertAttrToType(ParserSession session, Object outer, String s, ClassMeta<T> type) throws Exception { - if (s == null) - return null; - - if (type == null) - type = (ClassMeta<T>)object(); - PojoSwap transform = type.getPojoSwap(); - ClassMeta<?> sType = type.getSerializedClassMeta(); - - Object o = s; - if (sType.isChar()) - o = s.charAt(0); - else if (sType.isNumber()) - if (type.canCreateNewInstanceFromNumber(outer)) - o = type.newInstanceFromNumber(session, outer, parseNumber(s, type.getNewInstanceFromNumberClass())); - else - o = parseNumber(s, (Class<? extends Number>)sType.getInnerClass()); - else if (sType.isBoolean()) - o = Boolean.parseBoolean(s); - else if (! (sType.isCharSequence() || sType.isObject())) { - if (sType.canCreateNewInstanceFromString(outer)) - o = sType.newInstanceFromString(outer, s); - else - throw new ParseException(session, "Invalid conversion from string to class ''{0}''", type); - } - - if (transform != null) - o = transform.unswap(session, o, type); - - return (T)o; - } - - /** - * Convenience method for calling the {@link ParentProperty @ParentProperty} method on the specified object if it - * exists. - * - * @param cm The class type of the object. - * @param o The object. - * @param parent The parent to set. - * @throws Exception - */ - protected void setParent(ClassMeta<?> cm, Object o, Object parent) throws Exception { - Setter m = cm.getParentProperty(); - if (m != null) - m.set(o, parent); - } - - /** - * Convenience method for calling the {@link NameProperty @NameProperty} method on the specified object if it exists. - * - * @param cm The class type of the object. - * @param o The object. - * @param name The name to set. - * @throws Exception - */ - protected void setName(ClassMeta<?> cm, Object o, Object name) throws Exception { - if (cm != null) { - Setter m = cm.getNameProperty(); - if (m != null) - m.set(o, name); - } - } - - /** * Returns the media types handled based on the value of the {@link Consumes} annotation on the parser class. * * <p> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ParserContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/ParserContext.java b/juneau-core/src/main/java/org/apache/juneau/parser/ParserContext.java index e8fd4b7..583649f 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/ParserContext.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParserContext.java @@ -21,6 +21,12 @@ import org.apache.juneau.json.*; public class ParserContext extends BeanContext { /** + * Default context with all default values. + */ + @SuppressWarnings("hiding") + protected static final ParserContext DEFAULT = new ParserContext(PropertyStore.create()); + + /** * <b>Configuration property:</b> Trim parsed strings. * * <ul> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ParserGroup.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/ParserGroup.java b/juneau-core/src/main/java/org/apache/juneau/parser/ParserGroup.java index 846ebcb..d9ee830 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/ParserGroup.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParserGroup.java @@ -92,7 +92,7 @@ public final class ParserGroup { * tried to match against media types. */ public ParserGroup(PropertyStore propertyStore, Parser[] parsers) { - this.propertyStore = PropertyStore.create(propertyStore); + this.propertyStore = propertyStore.copy(); this.parsers = Collections.unmodifiableList(new ArrayList<Parser>(Arrays.asList(parsers))); List<MediaType> lmt = new ArrayList<MediaType>(); @@ -187,7 +187,7 @@ public final class ParserGroup { * @return A new copy of the property store passed in to the constructor. */ public PropertyStore createPropertyStore() { - return PropertyStore.create(propertyStore); + return propertyStore.copy(); } /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ParserInput.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/ParserInput.java b/juneau-core/src/main/java/org/apache/juneau/parser/ParserInput.java deleted file mode 100644 index 773606e..0000000 --- a/juneau-core/src/main/java/org/apache/juneau/parser/ParserInput.java +++ /dev/null @@ -1,230 +0,0 @@ -// *************************************************************************************************************************** -// * 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.parser; - -import static org.apache.juneau.internal.IOUtils.*; -import static org.apache.juneau.internal.StringUtils.*; - -import java.io.*; -import java.nio.charset.*; - -import org.apache.juneau.*; - -/** - * A wrapper around an object that a parser reads its input from. - * - * <p> - * For character-based parsers, the input object can be any of the following: - * <ul> - * <li>{@link Reader} - * <li>{@link CharSequence} - * <li>{@link InputStream} - * <li><code><jk>byte</jk>[]</code> - * <li>{@link File} - * <li><code><jk>null</jk></code> - * </ul> - * - * <p> - * For stream-based parsers, the input object can be any of the following: - * <ul> - * <li>{@link InputStream} - * <li><code><jk>byte</jk>[]</code> - * <li>{@link File} - * <li>{@link String} - Hex-encoded bytes. (not BASE-64!) - * <li><code><jk>null</jk></code> - * </ul> - */ -public class ParserInput { - - private Object input; - private String inputString; - private InputStream inputStream; - private Reader reader, noCloseReader; - private boolean debug, strict; - private String fileCharset, inputStreamCharset; - - /** - * Constructor. - * - * @param input - * @param debug - * @param strict - * @param fileCharset - * @param inputStreamCharset - */ - public ParserInput(Object input, boolean debug, boolean strict, String fileCharset, String inputStreamCharset) { - this.input = input; - this.debug = debug; - this.strict = strict; - this.fileCharset = fileCharset; - this.inputStreamCharset = inputStreamCharset; - } - - /** - * Wraps the specified input object inside an input stream. - * - * <p> - * Subclasses can override this method to implement their own input streams. - * - * @return The input object wrapped in an input stream, or <jk>null</jk> if the object is null. - * @throws ParseException If object could not be converted to an input stream. - */ - public InputStream getInputStream() throws ParseException { - try { - if (input == null) - return null; - if (input instanceof InputStream) { - if (debug) { - byte[] b = readBytes((InputStream)input, 1024); - inputString = toHex(b); - return new ByteArrayInputStream(b); - } - return (InputStream)input; - } - if (input instanceof byte[]) { - if (debug) - inputString = toHex((byte[])input); - return new ByteArrayInputStream((byte[])input); - } - if (input instanceof String) { - inputString = (String)input; - return new ByteArrayInputStream(fromHex((String)input)); - } - if (input instanceof File) { - if (debug) { - byte[] b = readBytes((File)input); - inputString = toHex(b); - return new ByteArrayInputStream(b); - } - inputStream = new FileInputStream((File)input); - return inputStream; - } - } catch (IOException e) { - throw new ParseException(e); - } - throw new ParseException("Cannot convert object of type {0} to an InputStream.", input.getClass().getName()); - } - - - /** - * Wraps the specified input object inside a reader. - * - * <p> - * Subclasses can override this method to implement their own readers. - * - * @return The input object wrapped in a Reader, or <jk>null</jk> if the object is null. - * @throws Exception If object could not be converted to a reader. - */ - public Reader getReader() throws Exception { - if (input == null) - return null; - if (input instanceof Reader) { - if (debug) { - inputString = read((Reader)input); - return new StringReader(inputString); - } - return (Reader)input; - } - if (input instanceof CharSequence) { - inputString = input.toString(); - if (reader == null) - reader = new ParserReader((CharSequence)input); - return reader; - } - if (input instanceof InputStream || input instanceof byte[]) { - InputStream is = ( - input instanceof InputStream - ? (InputStream)input - : new ByteArrayInputStream((byte[])input) - ); - if (noCloseReader == null) { - CharsetDecoder cd = ( - "default".equalsIgnoreCase(inputStreamCharset) - ? Charset.defaultCharset() - : Charset.forName(inputStreamCharset) - ).newDecoder(); - if (strict) { - cd.onMalformedInput(CodingErrorAction.REPORT); - cd.onUnmappableCharacter(CodingErrorAction.REPORT); - } else { - cd.onMalformedInput(CodingErrorAction.REPLACE); - cd.onUnmappableCharacter(CodingErrorAction.REPLACE); - } - noCloseReader = new InputStreamReader(is, cd); - } - if (debug) { - inputString = read(noCloseReader); - return new StringReader(inputString); - } - return noCloseReader; - } - if (input instanceof File) { - if (reader == null) { - CharsetDecoder cd = ( - "default".equalsIgnoreCase(fileCharset) - ? Charset.defaultCharset() - : Charset.forName(fileCharset) - ).newDecoder(); - if (strict) { - cd.onMalformedInput(CodingErrorAction.REPORT); - cd.onUnmappableCharacter(CodingErrorAction.REPORT); - } else { - cd.onMalformedInput(CodingErrorAction.REPLACE); - cd.onUnmappableCharacter(CodingErrorAction.REPLACE); - } - reader = new InputStreamReader(new FileInputStream((File)input), cd); - } - if (debug) { - inputString = read(reader); - return new StringReader(inputString); - } - return reader; - } - throw new ParseException("Cannot convert object of type {0} to a Reader.", input.getClass().getName()); - } - - /** - * Returns the raw input object passed into this session. - * - * @return The raw input object passed into this session. - */ - protected Object getRawInput() { - return input; - } - - /** - * Returns the input to this parser as a plain string. - * - * <p> - * This method only returns a value if {@link BeanContext#BEAN_debug} is enabled. - * - * @return The input as a string, or <jk>null</jk> if debug mode not enabled. - */ - public String getInputAsString() { - return inputString; - } - - /** - * Perform cleanup on this context object if necessary. - */ - public void close() { - try { - if (inputStream != null) - inputStream.close(); - if (reader != null) - reader.close(); - } catch (IOException e) { - throw new BeanRuntimeException(e); - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ParserListener.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/ParserListener.java b/juneau-core/src/main/java/org/apache/juneau/parser/ParserListener.java index 75d39bb..83ac9a7 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/ParserListener.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParserListener.java @@ -29,10 +29,11 @@ public class ParserListener { * Otherwise, the parser will throw a {@link ParseException}. * * @param <T> The class type of the bean. - * @param session - * The parser session. + * @param session The parser session. + * @param pipe + * The parser input. * Note that if {@link BeanContext#BEAN_debug} is enabled on the parser, you can get the input as a string through - * {@link ParserSession#getInputAsString()}. + * {@link ParserPipe#getInputAsString()}. * @param propertyName The property name encountered in the document. * @param beanClass The bean class. * @param bean The bean. @@ -41,8 +42,8 @@ public class ParserListener { * @param col * The column number where the unknown property was found (-1 if parser doesn't support line/column indicators). */ - public <T> void onUnknownBeanProperty(ParserSession session, String propertyName, Class<T> beanClass, T bean, int line, int col) { - onError(session, null, + public <T> void onUnknownBeanProperty(ParserSession session, ParserPipe pipe, String propertyName, Class<T> beanClass, T bean, int line, int col) { + onError(session, pipe, null, format("Unknown property ''{0}'' encountered while trying to parse into class ''{1}'' at line {2} column {3}", propertyName, beanClass, line, col) ); @@ -51,13 +52,15 @@ public class ParserListener { /** * Called when an error occurs during parsing but is ignored. * - * @param session The parsers session. + * @param session The parser session. + * @param pipe + * The parser input. * Note that if {@link BeanContext#BEAN_debug} is enabled on the parser, you can get the input as a string through - * {@link ParserSession#getInputAsString()}. + * {@link ParserPipe#getInputAsString()}. * @param t The throwable that was thrown by the getter method. * @param msg The error message. */ - public void onError(ParserSession session, Throwable t, String msg) { + public void onError(ParserSession session, ParserPipe pipe, Throwable t, String msg) { // Do something with this information. } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ParserPipe.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/ParserPipe.java b/juneau-core/src/main/java/org/apache/juneau/parser/ParserPipe.java new file mode 100644 index 0000000..4faf988 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParserPipe.java @@ -0,0 +1,278 @@ +// *************************************************************************************************************************** +// * 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.parser; + +import static org.apache.juneau.internal.IOUtils.*; +import static org.apache.juneau.internal.StringUtils.*; + +import java.io.*; +import java.nio.charset.*; + +import org.apache.juneau.*; +import org.apache.juneau.internal.*; + +/** + * A wrapper around an object that a parser reads its input from. + * + * <p> + * For character-based parsers, the input object can be any of the following: + * <ul> + * <li>{@link Reader} + * <li>{@link CharSequence} + * <li>{@link InputStream} + * <li><code><jk>byte</jk>[]</code> + * <li>{@link File} + * <li><code><jk>null</jk></code> + * </ul> + * + * <p> + * For stream-based parsers, the input object can be any of the following: + * <ul> + * <li>{@link InputStream} + * <li><code><jk>byte</jk>[]</code> + * <li>{@link File} + * <li>{@link String} - Hex-encoded bytes. (not BASE-64!) + * <li><code><jk>null</jk></code> + * </ul> + * + * <p> + * Note that Readers and InputStreams will NOT be automatically closed when {@link #close()} is called, but + * streams and readers created from other types (e.g. Files) WILL be automatically closed. + */ +public final class ParserPipe { + + private final Object input; + private final boolean debug, strict; + private final String fileCharset, inputStreamCharset; + + private String inputString; + private InputStream inputStream; + private Reader reader; + + /** + * Constructor. + * + * @param input The parser input object. + * @param debug + * If <jk>true</jk>, the input contents will be copied locally and accessible via the {@link #getInputAsString()} + * method. + * This allows the contents of the pipe to be accessed when a problem occurs. + * @param strict + * If <jk>true</jk>, sets {@link CodingErrorAction#REPORT} on {@link CharsetDecoder#onMalformedInput(CodingErrorAction)} + * and {@link CharsetDecoder#onUnmappableCharacter(CodingErrorAction)}. + * Otherwise, sets them to {@link CodingErrorAction#REPLACE}. + * @param fileCharset + * The charset to expect when reading from {@link File Files}. + * Use <js>"default"</js> to specify {@link Charset#defaultCharset()}. + * @param inputStreamCharset + * The charset to expect when reading from {@link InputStream InputStreams}. + * Use <js>"default"</js> to specify {@link Charset#defaultCharset()}. + */ + public ParserPipe(Object input, boolean debug, boolean strict, String fileCharset, String inputStreamCharset) { + this.input = input; + this.debug = debug; + this.strict = strict; + this.fileCharset = fileCharset; + this.inputStreamCharset = inputStreamCharset; + if (input instanceof CharSequence) + this.inputString = input.toString(); + } + + /** + * Shortcut constructor, typically for straight string input. + * + * <p> + * Equivalent to calling <code><jk>new</jk> ParserPipe(input, <jk>false</jk>, <jk>false</jk>, <jk>null</jk>, <jk>null</jk>);</code> + * + * @param input The input object. + */ + public ParserPipe(Object input) { + this(input, false, false, null, null); + } + + /** + * Wraps the specified input object inside an input stream. + * + * <p> + * Subclasses can override this method to implement their own input streams. + * + * @return The input object wrapped in an input stream, or <jk>null</jk> if the object is null. + * @throws IOException If object could not be converted to an input stream. + */ + public InputStream getInputStream() throws IOException { + if (input == null) + return null; + + if (input instanceof InputStream) { + if (debug) { + byte[] b = readBytes((InputStream)input, 1024); + inputString = toHex(b); + inputStream = new ByteArrayInputStream(b); + } else { + inputStream = (InputStream)input; + } + } else if (input instanceof byte[]) { + if (debug) + inputString = toHex((byte[])input); + inputStream = new ByteArrayInputStream((byte[])input); + } else if (input instanceof String) { + inputString = (String)input; + inputStream = new ByteArrayInputStream(fromHex((String)input)); + } else if (input instanceof File) { + if (debug) { + byte[] b = readBytes((File)input); + inputString = toHex(b); + inputStream = new ByteArrayInputStream(b); + } else { + inputStream = new FileInputStream((File)input); + } + } else { + throw new IOException("Cannot convert object of type "+input.getClass().getName()+" to an InputStream."); + } + + return inputStream; + } + + + /** + * Wraps the specified input object inside a reader. + * + * <p> + * Subclasses can override this method to implement their own readers. + * + * @return The input object wrapped in a Reader, or <jk>null</jk> if the object is null. + * @throws IOException If object could not be converted to a reader. + */ + public Reader getReader() throws IOException { + if (input == null) + return null; + + if (input instanceof Reader) { + if (debug) { + inputString = read((Reader)input); + reader = new StringReader(inputString); + } else { + reader = (Reader)input; + } + } else if (input instanceof CharSequence) { + inputString = input.toString(); + reader = new ParserReader(this); + } else if (input instanceof InputStream || input instanceof byte[]) { + InputStream is = ( + input instanceof InputStream + ? (InputStream)input + : new ByteArrayInputStream((byte[])input) + ); + CharsetDecoder cd = ( + "default".equalsIgnoreCase(inputStreamCharset) + ? Charset.defaultCharset() + : Charset.forName(inputStreamCharset) + ).newDecoder(); + if (strict) { + cd.onMalformedInput(CodingErrorAction.REPORT); + cd.onUnmappableCharacter(CodingErrorAction.REPORT); + } else { + cd.onMalformedInput(CodingErrorAction.REPLACE); + cd.onUnmappableCharacter(CodingErrorAction.REPLACE); + } + reader = new InputStreamReader(is, cd); + if (debug) { + inputString = read(reader); + reader = new StringReader(inputString); + } + } else if (input instanceof File) { + CharsetDecoder cd = ( + "default".equalsIgnoreCase(fileCharset) + ? Charset.defaultCharset() + : Charset.forName(fileCharset) + ).newDecoder(); + if (strict) { + cd.onMalformedInput(CodingErrorAction.REPORT); + cd.onUnmappableCharacter(CodingErrorAction.REPORT); + } else { + cd.onMalformedInput(CodingErrorAction.REPLACE); + cd.onUnmappableCharacter(CodingErrorAction.REPLACE); + } + reader = new InputStreamReader(new FileInputStream((File)input), cd); + if (debug) { + inputString = read(reader); + reader = new StringReader(inputString); + } + } else { + throw new IOException("Cannot convert object of type "+input.getClass().getName()+" to a Reader."); + } + + return reader; + } + + /** + * Returns the contents of this pipe as a buffered reader. + * + * <p> + * If the reader passed into this pipe is already a buffered reader, that reader will be returned. + * + * @return The contents of this pipe as a buffered reader. + * @throws Exception + */ + public Reader getBufferedReader() throws Exception { + return IOUtils.getBufferedReader(getReader()); + } + + /** + * Returns the input to this parser as a plain string. + * + * <p> + * This method only returns a value if {@link BeanContext#BEAN_debug} is enabled. + * + * @return The input as a string, or <jk>null</jk> if debug mode not enabled. + */ + public String getInputAsString() { + return inputString; + } + + /** + * Converts this pipe into a {@link ParserReader}. + * + * @return The converted pipe. + * @throws Exception + */ + public ParserReader getParserReader() throws Exception { + if (input == null) + return null; + if (input instanceof ParserReader) + reader = (ParserReader)input; + else + reader = new ParserReader(this); + return (ParserReader)reader; + } + + /** + * Returns <jk>true</jk> if the contents passed into this pipe was a {@link CharSequence}. + * + * @return <jk>true</jk> if the contents passed into this pipe was a {@link CharSequence}. + */ + public boolean isString() { + return inputString != null; + } + + /** + * Perform cleanup on this context object if necessary. + */ + public void close() { + try { + IOUtils.close(reader, inputStream); + } catch (IOException e) { + throw new BeanRuntimeException(e); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ParserReader.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/ParserReader.java b/juneau-core/src/main/java/org/apache/juneau/parser/ParserReader.java index daa1593..3c75e36 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/ParserReader.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParserReader.java @@ -14,6 +14,7 @@ package org.apache.juneau.parser; import java.io.*; +import org.apache.juneau.*; import org.apache.juneau.internal.*; /** @@ -32,7 +33,8 @@ import org.apache.juneau.internal.*; public class ParserReader extends Reader { /** Wrapped reader */ - protected Reader r; + protected final Reader r; + private final ParserPipe pipe; private char[] buff; // Internal character buffer private int line = 1; // Current line number @@ -42,33 +44,28 @@ public class ParserReader extends Reader { private int iEnd = 0; // The last good character position in the buffer private boolean endReached, holesExist; - ParserReader() {} - /** - * Constructor for input from a {@link CharSequence}. + * Constructor. * - * @param in The character sequence being read from. + * @param pipe The parser input. + * @throws IOException */ - public ParserReader(CharSequence in) { - this.r = new CharSequenceReader(in); - if (in == null) - this.buff = new char[0]; - else + public ParserReader(ParserPipe pipe) throws IOException { + this.pipe = pipe; + if (pipe.isString()) { + String in = pipe.getInputAsString(); + this.r = new CharSequenceReader(in); this.buff = new char[in.length() < 1024 ? in.length() : 1024]; + } else { + Reader _r = pipe.getReader(); + if (_r instanceof ParserReader) + this.r = ((ParserReader)_r).r; + else + this.r = _r; + this.buff = new char[1024]; + } } - /** - * Constructor for input from a {@link Reader}). - * - * @param r The Reader being wrapped. - */ - public ParserReader(Reader r) { - if (r instanceof ParserReader) - this.r = ((ParserReader)r).r; - else - this.r = r; - this.buff = new char[1024]; - } /** * Returns the current line number position in this reader. @@ -278,7 +275,7 @@ public class ParserReader extends Reader { * @return This object (for method chaining). * @throws IOException If a problem occurred trying to read from the reader. */ - public final ParserReader unread() throws IOException { + public ParserReader unread() throws IOException { if (iCurrent <= 0) throw new IOException("Buffer underflow."); iCurrent--; @@ -293,7 +290,8 @@ public class ParserReader extends Reader { */ @Override /* Reader */ public void close() throws IOException { - r.close(); + if (r != null) + r.close(); } /** @@ -417,4 +415,23 @@ public class ParserReader extends Reader { public int read(char[] cbuf, int off, int len) throws IOException { return r.read(cbuf, off, len); } + + /** + * Returns the combined location information on both this reader and the session. + * + * @param session The session object to read the last location on. + * @return A new map describing the current parse location. + */ + public ObjectMap getLocation(ParserSession session) { + return session.getLastLocation().append("line", getLine()).append("column", getColumn()); + } + + /** + * Returns the pipe that was passed into the constructor. + * + * @return The pipe that was passed into the constructor. + */ + public final ParserPipe getPipe() { + return pipe; + } }
