http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
new file mode 100644
index 0000000..de819bc
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
@@ -0,0 +1,317 @@
+/***************************************************************************************************************************
+ * 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.serializer;
+
+import java.io.*;
+import java.net.*;
+
+import org.apache.juneau.internal.*;
+
+/**
+ * Simple wrapper around a standard {@link Writer} with additional methods.
+ * <p>
+ * Modeled after the Java ProcessBuilder class so that you can chain commands 
to reduce
+ *     the need for string concatenation for performance reasons.
+ *
+ * <h6 class='topic'>Example</h6>
+ * <p class='bcode'>
+ *     writer.append(<js>"foo"</js>).nl().i(5).append(<js>"bar"</js>);
+ * </p>
+ *
+ * @author James Bognar ([email protected])
+ */
+public class SerializerWriter extends Writer {
+
+       /** The underlying writer. */
+       protected final Writer out;
+
+       /** Use-indentation flag. */
+       protected final boolean useIndentation;
+
+       /** Use-whitespace flag. */
+       protected final boolean useWhitespace;
+
+       /** Use-whitespace flag. */
+       protected final boolean trimStrings;
+
+       /** The quote character being used by this writer. */
+       protected final char quoteChar;
+
+       /** The base (e.g. <js>https://localhost:9443/contextPath";</js>) for 
relative URIs (e.g. <js>"my/path"</js>). */
+       protected final String relativeUriBase;
+
+       /** The base (e.g. <js>https://localhost:9443";</js>) for relative URIs 
with absolute paths (e.g. <js>"/contextPath/my/path"</js>). */
+       protected final String absolutePathUriBase;
+
+       /**
+        * @param out The writer being wrapped.
+        * @param useIndentation If <jk>true</jk>, calling {@link #cr(int)} 
will create an indentation.
+        * @param useWhitespace If <jk>true</jk>, calling {@link #s()} will 
write a space character.
+        * @param trimStrings If <jk>true</jk>, strings should be trimmed 
before they're serialized.
+        * @param quoteChar The character to write when {@link #q()} is called.
+        * @param relativeUriBase The base (e.g. 
<js>https://localhost:9443/contextPath";</js>) for relative URIs (e.g. 
<js>"my/path"</js>).
+        * @param absolutePathUriBase The base (e.g. 
<js>https://localhost:9443";</js>) for relative URIs with absolute paths (e.g. 
<js>"/contextPath/my/path"</js>).
+        */
+       public SerializerWriter(Writer out, boolean useIndentation, boolean 
useWhitespace, boolean trimStrings, char quoteChar, String relativeUriBase, 
String absolutePathUriBase) {
+               this.out = out;
+               this.useIndentation = useIndentation;
+               this.useWhitespace = useWhitespace;
+               this.trimStrings = trimStrings;
+               this.quoteChar = quoteChar;
+               this.relativeUriBase = relativeUriBase;
+               this.absolutePathUriBase = absolutePathUriBase;
+       }
+
+       /**
+        * Performs a carriage return.
+        * <p>
+        *      Adds a newline and the specified number of tabs (if the {@code 
useIndentation} setting is enabled) to the output.
+        *
+        * @param depth The indentation.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter cr(int depth) throws IOException {
+               if (useIndentation)
+                       return nl().i(depth);
+               return this;
+       }
+
+       /**
+        * Writes an indent (if the {@code useIndentation} setting is enabled), 
followed by text,
+        *      followed by a newline (if the {@code useIndentation} setting is 
enabled).
+        *
+        * @param indent The number of tabs to indent.
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object.
+        */
+       public SerializerWriter appendln(int indent, String text) throws 
IOException {
+               return append(indent, true, text);
+       }
+
+       /**
+        * Writes the specified text followed by a newline (if the {@code 
useIndentation} setting is enabled).
+        *
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object.
+        */
+       public SerializerWriter appendln(String text) throws IOException {
+               return append(0, true, text);
+       }
+
+       /**
+        * Writes an indent (if the {@code useIndentation} setting is enabled), 
followed by text.
+        *
+        * @param indent The number of tabs to indent.
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object.
+        */
+       public SerializerWriter append(int indent, String text) throws 
IOException {
+               return append(indent, false, text);
+       }
+
+       /**
+        * Writes an indent (if the {@code useIndentation} setting is enabled), 
followed by text.
+        *
+        * @param indent The number of tabs to indent.
+        * @param c The character to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object.
+        */
+       public SerializerWriter append(int indent, char c) throws IOException {
+               return i(indent).append(c);
+       }
+
+       /**
+        * Writes an indent (if the {@code useIndentation} setting is enabled), 
followed by text,
+        *      optionally followed by a newline (if the {@code useIndentation} 
setting is enabled).
+        *
+        * @param indent The number of tabs to indent.
+        * @param newline If <jk>true</jk>, then a newline is written.
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       private SerializerWriter append(int indent, boolean newline, String 
text) throws IOException {
+               i(indent);
+               out.write(text);
+               if (newline)
+                       nl();
+               return this;
+       }
+
+       /**
+        * Appends the specified object as a URI.
+        * <p>
+        * Object is converted to a <code>String</code> using 
<code>toString()</code>, so this will work on {@link URL} or {@link URI} 
objects,
+        * or any other type that returns a URI via it's 
<code>toString()</code> method.
+        * <p>
+        * If the URI is relative (i.e. without a schema and not prepended with 
<js>'/'</js>) the URI
+        * will be prepended with {@link #absolutePathUriBase} and {@link 
#relativeUriBase}.
+        * <p>
+        * If the URI is context-absolute (i.e. without a schema, but prepended 
with <js>'/'</js>)
+        * the URI will be prepended with {@link #absolutePathUriBase}.
+        *
+        * @param uri The URI to serialize.
+        * @return This object (for method chaining).
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        */
+       public SerializerWriter appendUri(Object uri) throws IOException {
+               String s = uri.toString();
+               if (s.indexOf("://") == -1) {
+                       if (StringUtils.startsWith(s, '/')) {
+                               if (absolutePathUriBase != null)
+                                       append(absolutePathUriBase);
+                       } else {
+                               if (relativeUriBase != null) {
+                                       append(relativeUriBase);
+                                       if (! relativeUriBase.equals("/"))
+                                               append("/");
+
+                               }
+                       }
+               }
+               return append(s);
+       }
+
+       /**
+        * Adds a whitespace character to the output if the {@code 
useWhitespace} setting is enabled.
+        *
+        * @return This object (for method chaining).
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        */
+       public SerializerWriter s() throws IOException {
+               if (useWhitespace)
+                       out.write(' ');
+               return this;
+       }
+
+       /**
+        * Adds the quote character specified by the {@code quoteChar} setting 
to the output.
+        *
+        * @return This object (for method chaining).
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        */
+       public SerializerWriter q() throws IOException {
+               out.write(quoteChar);
+               return this;
+       }
+
+       /**
+        * Writes an indent to the writer if the {@code useIndentation} setting 
is enabled.
+        *
+        * @param indent The number of tabs to indent.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter i(int indent) throws IOException {
+               if (useIndentation)
+                       for (int i = 0; i < indent; i++)
+                               out.write('\t');
+               return this;
+       }
+
+       /**
+        * Writes a newline to the writer if the {@code useIndentation} setting 
is enabled.
+        *
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter nl() throws IOException {
+               if (useIndentation)
+                       out.write('\n');
+               return this;
+       }
+
+       /**
+        * Writes the specified text to the writer.
+        *
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter append(Object text) throws IOException {
+               out.append(text == null ? null : text.toString());
+               return this;
+       }
+
+       /**
+        * Writes the specified text to the writer.
+        *
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter append(String text) throws IOException {
+               if (text != null)
+                       out.append(text);
+               return this;
+       }
+
+       /**
+        * Writes the specified text to the writer if b is true.
+        *
+        * @param b Boolean flag.
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter appendIf(boolean b, String text) throws 
IOException {
+               if (b)
+                       out.write(text);
+               return this;
+       }
+
+       /**
+        * Writes the specified text to the writer if b is true.
+        *
+        * @param b Boolean flag.
+        * @param c The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter appendIf(boolean b, char c) throws IOException {
+               if (b)
+                       out.write(c);
+               return this;
+       }
+
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Writer */
+       public SerializerWriter append(char c) throws IOException {
+               out.write(c);
+               return this;
+       }
+
+       @Override /* Writer */
+       public void write(char[] cbuf, int off, int len) throws IOException {
+               out.write(cbuf, off, len);
+       }
+
+       @Override /* Writer */
+       public void flush() throws IOException {
+               out.flush();
+       }
+
+       @Override /* Writer */
+       public void close() throws IOException {
+               out.close();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/serializer/StringObject.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/StringObject.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/StringObject.java
new file mode 100644
index 0000000..8d2dc07
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/StringObject.java
@@ -0,0 +1,85 @@
+/***************************************************************************************************************************
+ * 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.serializer;
+
+import java.io.*;
+
+import org.apache.juneau.*;
+
+/**
+ * A serializer/object pair used for delayed object serialization.
+ * <p>
+ * Useful in certain conditions such as logging when you don't want to 
needlessly serialize objects.
+ * <p>
+ * Instances of this method are created by the {@link 
WriterSerializer#toStringObject(Object)} method.
+ *
+ * <h6 class='topic'>Example</h6>
+ * <p class='bcode'>
+ *     <jc>// The POJO will not be serialized unless DEBUG is enabled.</jc>
+ *     logger.log(<jsf>DEBUG</jsf>, <js>"Object contents are: {0}"</js>, 
JsonSerializer.<jsf>DEFAULT</jsf>.toObjectString(myPojo));
+ * </p>
+ *
+ * @author James Bognar ([email protected])
+ */
+public class StringObject implements CharSequence, Writable {
+
+       private final WriterSerializer s;
+       private final Object o;
+       private String results;
+
+       /**
+        * Constructor.
+        * @param s The serializer to use to serialize the object.
+        * @param o The object to be serialized.
+        */
+       public StringObject(WriterSerializer s, Object o) {
+               this.s = s;
+               this.o = o;
+       }
+
+       @Override /* Object */
+       public String toString() {
+               if (results == null)
+                       results = s.toString(o);
+               return results;
+       }
+
+       @Override /* CharSequence */
+       public int length() {
+               return toString().length();
+       }
+
+       @Override /* CharSequence */
+       public char charAt(int index) {
+               return toString().charAt(index);
+       }
+
+       @Override /* CharSequence */
+       public CharSequence subSequence(int start, int end) {
+               return toString().subSequence(start, end);
+       }
+
+       @Override /* Writable */
+       public void writeTo(Writer w) throws IOException {
+               try {
+                       s.serialize(o, w);
+               } catch (SerializeException e) {
+                       throw new IOException(e);
+               }
+       }
+
+       @Override /* Writable */
+       public String getMediaType() {
+               return s.getMediaTypes()[0];
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
new file mode 100644
index 0000000..49bec69
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
@@ -0,0 +1,96 @@
+/***************************************************************************************************************************
+ * 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.serializer;
+
+import java.io.*;
+import java.lang.reflect.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+
+/**
+ * Subclass of {@link Serializer} for character-based serializers.
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ *     This class is typically the parent class of all character-based 
serializers.
+ *     It has 2 abstract methods to implement...
+ * <ul class='spaced-list'>
+ *     <li>{@link #createSession(Object, ObjectMap, Method)}
+ *     <li>{@link #doSerialize(SerializerSession, Object)}
+ * </ul>
+ *
+ *
+ * <h6 class='topic'>@Produces annotation</h6>
+ * <p>
+ *     The media types that this serializer can produce is specified through 
the {@link Produces @Produces} annotation.
+ * <p>
+ *     However, the media types can also be specified programmatically by 
overriding the {@link #getMediaTypes()}
+ *             and {@link #getResponseContentType()} methods.
+ *
+ * @author James Bognar ([email protected])
+ */
+public abstract class WriterSerializer extends Serializer {
+
+       @Override /* Serializer */
+       public boolean isWriterSerializer() {
+               return true;
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Other methods
+       
//--------------------------------------------------------------------------------
+
+       /**
+        * Convenience method for serializing an object to a 
<code>String</code>.
+        *
+        * @param o The object to serialize.
+        * @return The output serialized to a string.
+        * @throws SerializeException If a problem occurred trying to convert 
the output.
+        */
+       @Override
+       public final String serialize(Object o) throws SerializeException {
+               StringWriter w = new StringWriter();
+               serialize(createSession(w), o);
+               return w.toString();
+       }
+
+       /**
+        * Identical to {@link #serialize(Object)} except throws a {@link 
RuntimeException}
+        * instead of a {@link SerializeException}.
+        * This is typically good enough for debugging purposes.
+        *
+        * @param o The object to serialize.
+        * @return The serialized object.
+        */
+       public final String toString(Object o) {
+               try {
+                       StringWriter w = new StringWriter();
+                       serialize(createSession(w), o);
+                       return w.toString();
+               } catch (Exception e) {
+                       throw new RuntimeException(e);
+               }
+       }
+
+       /**
+        * Wraps the specified object inside a {@link StringObject}.
+        *
+        * @param o The object to wrap.
+        * @return The wrapped object.
+        */
+       public final StringObject toStringObject(Object o) {
+               return new StringObject(this, o);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/serializer/package.html
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/package.html 
b/juneau-core/src/main/java/org/apache/juneau/serializer/package.html
new file mode 100644
index 0000000..d19a959
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/package.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<!--
+/***************************************************************************************************************************
+ * 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.
+ *
+ 
***************************************************************************************************************************/
+ -->
+<html>
+<head>
+       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+       <style type="text/css">
+               /* For viewing in Page Designer */
+               @IMPORT url("../../../../../../javadoc.css");
+
+               /* For viewing in REST interface */
+               @IMPORT url("../htdocs/javadoc.css");
+               body { 
+                       margin: 20px; 
+               }       
+       </style>
+       <script>
+               /* Replace all @code and @link tags. */ 
+               window.onload = function() {
+                       document.body.innerHTML = 
document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>');
+                       document.body.innerHTML = 
document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, 
'<code>$3</code>');
+               }
+       </script>
+</head>
+<body>
+<p>Serializer API</p>
+
+<script>
+       function toggle(x) {
+               var div = x.nextSibling;
+               while (div != null && div.nodeType != 1)
+                       div = div.nextSibling;
+               if (div != null) {
+                       var d = div.style.display;
+                       if (d == 'block' || d == '') {
+                               div.style.display = 'none';
+                               x.className += " closed";
+                       } else {
+                               div.style.display = 'block';
+                               x.className = 
x.className.replace(/(?:^|\s)closed(?!\S)/g , '' );
+                       }
+               }
+       }
+</script>
+
+<a id='TOC'></a><h5 class='toc'>Table of Contents</h5>
+<ol class='toc'>
+       <li><p><a class='doclink' href='#Serializer'>Serializer API</a></p>
+       <ol>
+               <li><p><a class='doclink' href='#SerializerGroup'>The 
SerializerGroup class</a></p>
+       </ol>
+       <li><p><a class='doclink' href='#DefiningSerializer'>Defining a new 
Serializer</a></p>
+</ol>
+
+<!-- 
========================================================================================================
 -->
+<a id="Serializer"></a>
+<h2 class='topic' onclick='toggle(this)'>1 - Serializer API</h2>
+<div class='topic'>
+       <p>
+               The serialization API is designed to be easily extensible by 
developers.<br>
+               If you are writing your own serializer, you will typically 
subclass directly from either {@link 
org.apache.juneau.serializer.WriterSerializer}
+                       or {@link 
org.apache.juneau.serializer.OutputStreamSerializer}.<br>
+       </p>
+
+       <!-- 
========================================================================================================
 -->
+       <a id="SerializerGroup"></a>
+       <h3 class='topic' onclick='toggle(this)'>1.1 - The SerializerGroup 
class</h3>
+       <div class='topic'>
+               <p>
+                       The {@link 
org.apache.juneau.serializer.SerializerGroup} class represents a group of 
serializers registered with the media types they handle.
+               </p>
+               
+               <h6 class='topic'>Features</h6>         
+               <p>
+                       The <code>SerializerGroup</code> class provides the 
following features:
+               <ul class='spaced-list'>
+                       <li>Finds serializers based on HTTP <code>Accept</code> 
header values.
+                       <li>Sets common properties on all serializers in a 
single method call.
+                       <li>Locks all serializers in a single method call.
+                       <li>Clones existing groups and all serializers within 
the group in a single method call.
+               </ul>
+               
+               <p>
+                       Refer to {@link 
org.apache.juneau.serializer.SerializerGroup} for additional information.
+               </p>
+       </div> 
+</div>
+
+
+<!-- 
========================================================================================================
 -->
+<a id="DefiningSerializer"></a>
+<h2 class='topic' onclick='toggle(this)'>2 - Defining a new Serializer</h2>
+<div class='topic'>
+       <p>
+               Defining a new serializer is quite simple if you subclass 
directly from {@link org.apache.juneau.serializer.WriterSerializer} 
+                       or {@link 
org.apache.juneau.serializer.OutputStreamSerializer}.<br>
+               In each case, you simply need to implement a single
+                       method and specify a {@link 
org.apache.juneau.annotation.Produces} annotation.
+       </p>
+       <p>
+               The following example shows a simple serializer that converts 
images to output streams using standard JRE classes.
+       </p>
+       <p class='bcode'>
+       <ja>@Produces</ja>({<js>"image/png"</js>,<js>"image/jpeg"</js>})
+       <jk>public static class</jk> ImageSerializer <jk>extends</jk> 
OutputStreamSerializer {
+               <ja>@Override</ja>
+               <jk>public void</jk> serialize(Object o, OutputStream out, 
SerializerSession session) <jk>throws</jk> IOException, SerializeException {
+                       RenderedImage image = (RenderedImage)o;
+                       String mediaType = ctx.getMediaType();
+                       ImageIO.<jsm>write</jsm>(image, 
mediaType.substring(mediaType.indexOf(<js>'/'</js>)+1), out);
+               }       
+       }
+       </p>
+       <p>
+               Serializer that take advantage of the entire {@link 
org.apache.juneau.CoreApi} interface to be able to serialize arbitrary beans 
and POJOs is
+                       considerably more complex and outside the scope of this 
document.<br>  
+               If developing such a serializer, the best course of action 
would be to replicate what occurs in the {@link 
org.apache.juneau.json.JsonSerializer} class.
+       </p>
+</div>
+
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java 
b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
new file mode 100644
index 0000000..946f02b
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
@@ -0,0 +1,78 @@
+/***************************************************************************************************************************
+ * 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.soap;
+
+import static org.apache.juneau.soap.SoapXmlSerializerContext.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.xml.*;
+
+/**
+ * Serializes POJOs to HTTP responses as XML+SOAP.
+ *
+ *
+ * <h6 class='topic'>Media types</h6>
+ * <p>
+ *     Handles <code>Accept</code> types: <code>text/xml+soap</code>
+ * <p>
+ *     Produces <code>Content-Type</code> types: <code>text/xml+soap</code>
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ *     Essentially the same output as {@link XmlDocSerializer}, except wrapped 
in a standard SOAP envelope.
+ *
+ *
+ * <h6 class='topic'>Configurable properties</h6>
+ * <p>
+ *     This class has the following properties associated with it:
+ * <ul>
+ *     <li>{@link SoapXmlSerializerContext}
+ *     <li>{@link BeanContext}
+ * </ul>
+ *
+ *
+ * @author James Bognar ([email protected])
+ */
+@Produces(value="text/xml+soap",contentType="text/xml")
+public final class SoapXmlSerializer extends XmlSerializer {
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Serializer */
+       protected void doSerialize(SerializerSession session, Object o) throws 
Exception {
+               XmlSerializerSession s = (XmlSerializerSession)session;
+               XmlWriter w = s.getWriter();
+               w.append("<?xml")
+                       .attr("version", "1.0")
+                       .attr("encoding", "UTF-8")
+                       .appendln("?>");
+               w.oTag("soap", "Envelope")
+                       .attr("xmlns", "soap", 
s.getProperties().getString(SOAPXML_SOAPAction, 
"http://www.w3.org/2003/05/soap-envelope";))
+                       .appendln(">");
+               w.sTag(1, "soap", "Body").nl();
+               super.serialize(s, o);
+               w.eTag(1, "soap", "Body").nl();
+               w.eTag("soap", "Envelope").nl();
+       }
+
+       @Override /* Serializer */
+       public ObjectMap getResponseHeaders(ObjectMap properties) {
+               return super.getResponseHeaders(properties)
+                       .append("SOAPAction", 
properties.getString(SOAPXML_SOAPAction, 
"http://www.w3.org/2003/05/soap-envelope";));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java
 
b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java
new file mode 100644
index 0000000..b799afa
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java
@@ -0,0 +1,28 @@
+/***************************************************************************************************************************
+ * 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.soap;
+
+/**
+ * Properties associated with the {@link SoapXmlSerializer} class.
+ *
+ * @author James Bognar ([email protected])
+ */
+public final class SoapXmlSerializerContext {
+
+       /**
+        * The <code>SOAPAction</code> HTTP header value to set on responses.
+        * <p>
+        * Default is <js>"http://www.w3.org/2003/05/soap-envelope";</js>.
+        */
+       public static final String SOAPXML_SOAPAction = 
"SoapXmlSerializer.SOAPAction";
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/soap/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/soap/package.html 
b/juneau-core/src/main/java/org/apache/juneau/soap/package.html
new file mode 100644
index 0000000..89463d6
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/soap/package.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<!--
+/***************************************************************************************************************************
+ * 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.
+ *
+ 
***************************************************************************************************************************/
+ -->
+<html>
+<head>
+       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+       <style type="text/css">
+               /* For viewing in Page Designer */
+               @IMPORT url("../../../../../../javadoc.css");
+
+               /* For viewing in REST interface */
+               @IMPORT url("../htdocs/javadoc.css");
+               body { 
+                       margin: 20px; 
+               }       
+       </style>
+       <script>
+               /* Replace all @code and @link tags. */ 
+               window.onload = function() {
+                       document.body.innerHTML = 
document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>');
+                       document.body.innerHTML = 
document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, 
'<code>$3</code>');
+               }
+       </script>
+</head>
+<body>
+<p>SOAP/XML serialization and parsing support</p>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/DefaultingVar.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/DefaultingVar.java 
b/juneau-core/src/main/java/org/apache/juneau/svl/DefaultingVar.java
new file mode 100644
index 0000000..6004c93
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/svl/DefaultingVar.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.svl;
+
+import org.apache.juneau.internal.*;
+
+/**
+ * Interface for the resolution of vars with a default value if the 
<code>resolve()</code> method returns <jk>null</jk>.
+ * <p>
+ * For example, to resolve the system property <js>"myProperty"</js> but 
resolve to <js>"not found"</js> if the property doesn't exist:
+ *     <js>"$S{myProperty,not found}"</js>
+ * <p>
+ * Subclasses must implement the {@link #resolve(VarResolverSession, String)} 
method.
+ *
+ * @see org.apache.juneau.svl
+ * @author James Bognar ([email protected])
+ */
+public abstract class DefaultingVar extends SimpleVar {
+
+       /**
+        * Constructor.
+        *
+        * @param name The name of this variable.
+        */
+       public DefaultingVar(String name) {
+               super(name);
+       }
+
+       @Override /* Var*/
+       public String doResolve(VarResolverSession session, String s) {
+               int i = s.indexOf(',');
+               if (i == -1)
+                       return resolve(session, s);
+               String[] s2 = StringUtils.split(s, ',');
+               String v = resolve(session, s2[0]);
+               if (v == null)
+                       v = s2[1];
+               return v;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/MapVar.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/MapVar.java 
b/juneau-core/src/main/java/org/apache/juneau/svl/MapVar.java
new file mode 100644
index 0000000..cc0a385
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/svl/MapVar.java
@@ -0,0 +1,46 @@
+/***************************************************************************************************************************
+ * 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.svl;
+
+import java.util.*;
+
+/**
+ * A subclass of {@link DefaultingVar} that simply pulls values from a {@link 
Map}.
+ *
+ * @see org.apache.juneau.svl
+ * @author James Bognar ([email protected])
+ */
+@SuppressWarnings("rawtypes")
+public abstract class MapVar extends DefaultingVar {
+
+       private final Map m;
+
+       /**
+        * Constructor.
+        *
+        * @param name The name of this variable.
+        * @param m The map to pull values from.
+        */
+       public MapVar(String name, Map m) {
+               super(name);
+               if (m == null)
+                       throw new IllegalArgumentException("''m'' parameter 
cannot be null.");
+               this.m = m;
+       }
+
+       @Override /* Var */
+       public String resolve(VarResolverSession session, String varVal) {
+               Object o = m.get(varVal);
+               return (o == null ? null : o.toString());
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/MultipartVar.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/MultipartVar.java 
b/juneau-core/src/main/java/org/apache/juneau/svl/MultipartVar.java
new file mode 100644
index 0000000..194c642
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/svl/MultipartVar.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.svl;
+
+import org.apache.juneau.internal.*;
+
+/**
+ * Interface for the resolution of vars that consist of a comma-delimited list.
+ * <p>
+ * (e.g. <js>"$X{foo, bar, baz}"</js>)
+ *
+ * @see org.apache.juneau.svl
+ * @author James Bognar ([email protected])
+ */
+public abstract class MultipartVar extends SimpleVar {
+
+       /**
+        * Constructor.
+        *
+        * @param name The name of this variable.
+        */
+       public MultipartVar(String name) {
+               super(name);
+       }
+
+       /**
+        * The interface that needs to be implemented for this interface.
+        *
+        * @param session The session object used for a single instance of a 
string resolution.
+        * @param args The arguments inside the variable.
+        * @return The resolved variable.
+        */
+       public abstract String resolve(VarResolverSession session, String[] 
args);
+
+       @Override /* Var */
+       public String resolve(VarResolverSession session, String s) {
+               String[] s2 = s.indexOf(',') == -1 ? new String[]{s} : 
StringUtils.split(s, ',');
+               return resolve(session, s2);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/SimpleVar.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/SimpleVar.java 
b/juneau-core/src/main/java/org/apache/juneau/svl/SimpleVar.java
new file mode 100644
index 0000000..9b5a2c2
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/svl/SimpleVar.java
@@ -0,0 +1,46 @@
+/***************************************************************************************************************************
+ * 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.svl;
+
+import java.io.*;
+
+/**
+ * Abstract superclass of all Simple Var Language variables that resolve to 
simple returned string values.
+ * <p>
+ * Note the difference between this class and {@link StreamedVar} that streams
+ *     values to writers.
+ * Unlike the {@link StreamedVar} class, the returned value from this class can
+ *     contain nested variables that will be recursively resolved by {@link 
VarResolver}.
+ * <p>
+ * Subclasses must implement the {@link #resolve(VarResolverSession, String)} 
method.
+ *
+ * @see org.apache.juneau.svl
+ * @author James Bognar ([email protected])
+ */
+public abstract class SimpleVar extends Var {
+
+       /**
+        * Constructor.
+        *
+        * @param name The variable name (e.g. <js>"C"</js> for variables of 
the form <js>"$C{...}"</js>)
+        */
+       protected SimpleVar(String name) {
+               super(name, false);
+       }
+
+       @Override /* Var */
+       public void resolveTo(VarResolverSession session, Writer w, String arg) 
{
+               throw new UnsupportedOperationException("Cannot call streamTo() 
on SimpleVar class");
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/StreamedVar.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/StreamedVar.java 
b/juneau-core/src/main/java/org/apache/juneau/svl/StreamedVar.java
new file mode 100644
index 0000000..75849bd
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/svl/StreamedVar.java
@@ -0,0 +1,44 @@
+/***************************************************************************************************************************
+ * 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.svl;
+
+/**
+ * Abstract superclass of all Simple Var Language variables that write their 
values directly to a writer.
+ * <p>
+ * Note the difference between this class and {@link SimpleVar} that returns 
simple string values.
+ * Unlike the {@link SimpleVar} class, the output from this class cannot 
contain nested variables.
+ * However, this class can be more efficient for variables that produce large 
amounts of output
+ *     so that the creation of large in-memory strings is avoided.
+ * <p>
+ * Subclasses must implement the {@link #resolveTo(VarResolverSession, 
java.io.Writer, String)} method.
+ *
+ * @see org.apache.juneau.svl
+ * @author James Bognar ([email protected])
+ */
+public abstract class StreamedVar extends Var {
+
+       /**
+        * Constructor.
+        *
+        * @param name The variable name (e.g. <js>"C"</js> for variables of 
the form <js>"$C{...}"</js>)
+        */
+       public StreamedVar(String name) {
+               super(name, true);
+       }
+
+       @Override /* Var */
+       public String resolve(VarResolverSession session, String arg) {
+               throw new UnsupportedOperationException("Cannot call resolve() 
on StreamedVar class");
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/Var.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/Var.java 
b/juneau-core/src/main/java/org/apache/juneau/svl/Var.java
new file mode 100644
index 0000000..4d693be
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/svl/Var.java
@@ -0,0 +1,110 @@
+/***************************************************************************************************************************
+ * 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.svl;
+
+import static org.apache.juneau.internal.ThrowableUtils.*;
+
+import java.io.*;
+
+/**
+ * Abstract superclass of all Simple Var Language variables.
+ * <p>
+ * Vars are used to convert simple variables of the form 
<js>"$varName{varKey}"</js> into
+ *     something else by the {@link VarResolver} class.
+ * <p>
+ * Subclasses must implement one of the following two methods:
+ * <ul>
+ *     <li>{@link #resolve(VarResolverSession,String)} - For simple vars.
+ *     <li>{@link #resolveTo(VarResolverSession,Writer,String)} - For streamed 
vars.
+ * </ul>
+ * Subclasses MUST implement a no-arg constructor so that class names can be 
passed to the {@link VarResolver#addVars(Class...)} method.
+ * They must also be thread safe!
+ * <p>
+ * Two direct abstract subclasses are provided to differentiated between 
simple and streamed vars:
+ * <ul>
+ *     <li>{@link SimpleVar}
+ *     <li>{@link StreamedVar}
+ * </ul>
+ *
+ * @see org.apache.juneau.svl
+ * @author James Bognar ([email protected])
+ */
+public abstract class Var {
+
+       private final String name;
+       final boolean streamed;
+
+       /**
+        * Constructor.
+        *
+        * @param name The name of this variable.
+        * @param streamed Whether this variable is 'streamed', meaning the 
{@link #resolveTo(VarResolverSession, Writer, String)}
+        *      method is implemented.  If <jk>false</jk>, then the {@link 
#resolve(VarResolverSession, String)} method is implemented.
+        */
+       public Var(String name, boolean streamed) {
+               this.name = name;
+               this.streamed = streamed;
+
+               if (name == null)
+                       illegalArg("Invalid var name.  Must consist of only 
uppercase and lowercase ASCII letters.");
+               else for (int i = 0; i < name.length(); i++) {
+               // Need to make sure only ASCII characters are used.
+                       char c = name.charAt(i);
+                       if (c < 'A' || c > 'z' || (c > 'Z' && c < 'a'))
+                               illegalArg("Invalid var name.  Must consist of 
only uppercase and lowercase ASCII letters.");
+               }
+       }
+
+       /**
+        * Return the name of this variable.
+        * <p>
+        * For example, the system property variable returns <js>"S"</js> since 
the format of the
+        *      variable is <js>"$S{system.property}"</js>.
+        *
+        * @return The name of this variable.
+        */
+       protected String getName() {
+               return name;
+       }
+
+       /**
+        * The method called from {@link VarResolver}.
+        * Can be overridden to intercept the request and do special handling.
+        * Default implementation simply calls resolve(String).
+        *
+        * @param session The session object used for a single instance of a 
string resolution.
+        * @param arg The inside argument of the variable.
+        * @return The resolved value.
+        */
+       protected String doResolve(VarResolverSession session, String arg) {
+               return resolve(session, arg);
+       }
+
+       /**
+        * The interface that needs to be implemented for subclasses of {@link 
SimpleVar}.
+        *
+        * @param session The session object used for a single instance of a 
var resolution.
+        * @param arg The inside argument of the variable.
+        * @return The resolved value.
+        */
+       public abstract String resolve(VarResolverSession session, String arg);
+
+       /**
+        * The interface that needs to be implemented for subclasses of {@link 
StreamedVar}.
+        *
+        * @param session The session object used for a single instance of a 
var resolution.
+        * @param w The writer to send the resolved value to.
+        * @param arg The inside argument of the variable.
+        */
+       public abstract void resolveTo(VarResolverSession session, Writer w, 
String arg);
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/VarResolver.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/VarResolver.java 
b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolver.java
new file mode 100644
index 0000000..5c6c7f1
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolver.java
@@ -0,0 +1,221 @@
+/***************************************************************************************************************************
+ * 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.svl;
+
+import static java.text.MessageFormat.*;
+import static org.apache.juneau.svl.VarResolverContext.*;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.ini.*;
+import org.apache.juneau.svl.vars.*;
+
+/**
+ * Utility class for resolving variables of the form <js>"$X{key}"</js> in 
strings.
+ * <p>
+ * Variables are of the form <code>$X{key}</code>, where <code>X</code> can 
consist of zero or more ASCII characters.<br>
+ *     The variable key can contain anything, even nested variables that get 
recursively resolved.
+ * <p>
+ * Variables are defined through the {@link #addVars(Class[])} method.
+ * <p>
+ * The {@link Var} interface defines how variables are converted to values.
+ * <p>
+ * <h6 class='topic'>Example:</h6>
+ * <p class='bcode'>
+ *     <jk>public class</jk> SystemPropertiesVar <jk>extends</jk> SimpleVar {
+ *
+ *             <jc>// Must have a no-arg constructor!</jc>
+ *             <jk>public</jk> SystemPropertiesVar() {
+ *                     <jk>super</jk>(<js>"S"</js>);
+ *             }
+ *
+ *             <ja>@Override</ja>
+ *             <jk>public</jk> String resolve(VarResolverSession session, 
String varVal) {
+ *                             <jk>return</jk> 
System.<jsm>getProperty</jsm>(varVal);
+ *             }
+ *     }
+ *
+ *     <jc>// Create a variable resolver that resolves system properties (e.g. 
"$S{java.home}")</jc>
+ *     VarResolver r = <jk>new</jk> 
VarResolver().addVars(SystemPropertiesVar.<js>class</js>);
+ *
+ *     <jc>// Use it!</jc>
+ *     System.<jsf>out</jsf>.println(r.resolve(<js>"java.home is set to 
$S{java.home}"</js>));
+ * </p>
+ *
+ * <h6 class='topic'>Context objects</h6>
+ * <p>
+ * Var resolvers can have zero or more context objects associated with them.
+ * <p>
+ * Context objects are arbitrary objects associated with this var resolver, 
such as
+ *     a {@link ConfigFile} object.
+ * They are set through the {@link #setContextObject(String, Object)} method.
+ * They can be any class type.
+ * <p>
+ * Context objects can be retrieved by {@link Var} classes through the {@link 
VarResolverSession#getSessionObject(Class, String)} method.
+ *
+ * <h6 class='topic'>Session objects</h6>
+ * <p>
+ * Session objects are considered more ephemeral than context objects.
+ * While a context object is unlikely to ever change, a session object may 
change on every
+ *     use of the var resolver.
+ * For example, the server API defines various <code>Var</code> objects that 
use the <code>RestRequest</code>
+ *     object as a session object for the duration of a single HTTP request.
+ * <p>
+ * Session objects are used by calling the {@link #createSession()} or {@link 
#createSession(Map)} methods to create an instance
+ *     of a {@link VarResolverSession} object that contains {@link 
VarResolverSession#resolve(String)} and {@link 
VarResolverSession#resolveTo(String,Writer)} methods
+ *     that are identical to {@link VarResolver#resolve(String)} and {@link 
VarResolver#resolveTo(String, Writer)} except that the <code>Var</code> objects
+ *     have access to the session objects through the {@link 
VarResolverSession#getSessionObject(Class, String)} method.
+ * <p>
+ * Session objects are specified through either the {@link 
#createSession(Map)} method or the {@link 
VarResolverSession#setSessionObject(String, Object)} methods.
+ *
+ * <h6 class='topic'>Cloning</h6>
+ * <p>
+ * Var resolvers can be cloned by using the {@link #clone()} method.
+ * Cloning a resolver will copy it's {@link Var} class names and context 
objects.
+ * <p>
+ * <h6 class='topic'>Example:</h6>
+ * <p class='bcode'>
+ *     <jc>// Create a resolver that copies the default resolver and adds $C 
and $ARG vars.</jc>
+ *     VarResolver myVarResolver = 
VarResolver.<jsf>DEFAULT</jsf>.clone().addVars(ConfigVar.<jk>class</jk>, 
ArgsVar.<jk>class</jk>);
+ * </p>
+ *
+ * @see org.apache.juneau.svl
+ * @author James Bognar ([email protected])
+ */
+public class VarResolver extends CoreApi {
+
+       /**
+        * Default string variable resolver with support for system properties 
and environment variables:
+        * <p>
+        * <ul>
+        *      <li><code>$S{key}</code>,<code>$S{key,default}</code> - System 
properties.
+        *      <li><code>$E{key}</code>,<code>$E{key,default}</code> - 
Environment variables.
+        * </ul>
+        *
+        * @see SystemPropertiesVar
+        * @see EnvVariablesVar
+        */
+       public static final VarResolver DEFAULT = new 
VarResolver().addVars(SystemPropertiesVar.class, EnvVariablesVar.class).lock();
+
+       /**
+        * Construct an empty var resolver with no vars.
+        */
+       public VarResolver() {}
+
+       /**
+        * Register new variables with this resolver.
+        *
+        * @param vars The variable resolver classes.
+        *      These classes must subclass from {@link Var} and have no-arg 
constructors.
+        * @return This object (for method chaining).
+        */
+       public VarResolver addVars(Class<?>...vars) {
+               checkLock();
+               ContextFactory cf = getContextFactory();
+               for (Class<?> v : vars) {
+                       try {
+                               v.newInstance();
+                       } catch (InstantiationException e) {
+                               throw new 
UnsupportedOperationException(format("Cannot instantiate variable class {0}.  
Must have a public no-arg constructor.", v.getName()));
+                       } catch (RuntimeException e) {
+                               throw e;
+                       } catch (Exception e) {
+                               throw new RuntimeException(e);
+                       }
+                       cf.addToProperty(SVL_vars, v);
+               }
+               return this;
+       }
+
+       /**
+        * Associates a context object with this resolver.
+        *
+        * @param name The name of the context object.
+        * @param object The context object.
+        * @return This object (for method chaining).
+        */
+       public VarResolver setContextObject(String name, Object object) {
+               getContextFactory().putToProperty(SVL_context, name, object);
+               return this;
+       }
+
+       /**
+        * Creates a new resolver session with no session objects.
+        * <p>
+        * Session objects can be associated with the specified session using 
the {@link VarResolverSession#setSessionObject(String, Object)} method.
+        *
+        * @return A new resolver session.
+        */
+       public VarResolverSession createSession() {
+               return new 
VarResolverSession(getContext(VarResolverContext.class), null);
+       }
+
+       /**
+        * Same as {@link #createSession()} except allows you to specify 
session objects as a map.
+        *
+        * @param sessionObjects The session objects to associate with the 
sessino.
+        * @return A new resolver session.
+        */
+       public VarResolverSession createSession(Map<String,Object> 
sessionObjects) {
+               return new 
VarResolverSession(getContext(VarResolverContext.class), sessionObjects);
+       }
+
+       /**
+        * Resolve variables in the specified string.
+        * <p>
+        * This is a shortcut for calling 
<code>createSession(<jk>null</jk>).resolve(s);</code>.
+        * This method can only be used if the string doesn't contain variables 
that rely on the existence of session variables.
+        *
+        * @param s The input string.
+        * @return The string with variables resolved, or the same string if it 
doesn't contain any variables to resolve.
+        */
+       public String resolve(String s) {
+               return createSession(null).resolve(s);
+       }
+
+       /**
+        * Resolve variables in the specified string and sends the results to 
the specified writer.
+        * <p>
+        * This is a shortcut for calling 
<code>createSession(<jk>null</jk>).resolveTo(s, w);</code>.
+        * This method can only be used if the string doesn't contain variables 
that rely on the existence of session variables.
+        *
+        * @param s The input string.
+        * @param w The writer to send the result to.
+        * @throws IOException
+        */
+       public void resolveTo(String s, Writer w) throws IOException {
+               createSession(null).resolveTo(s, w);
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Lockable */
+       public VarResolver lock() {
+               super.lock();
+               return this;
+       }
+
+       @Override /* Lockable */
+       public VarResolver clone() {
+               try {
+                       return (VarResolver)super.clone();
+               } catch (CloneNotSupportedException e) {
+                       throw new RuntimeException(e);
+               }
+       }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverContext.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverContext.java 
b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverContext.java
new file mode 100644
index 0000000..d4acd0b
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverContext.java
@@ -0,0 +1,113 @@
+/***************************************************************************************************************************
+ * 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.svl;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Configurable properties on the {@link VarResolver} class.
+ *     <p>
+ *     Used to associate {@link Var Vars} and context objects with {@link 
VarResolver VarResolvers}.
+ * <p>
+ * See {@link ContextFactory} for more information about context properties.
+ *
+ * @see org.apache.juneau.svl
+ * @author James Bognar ([email protected])
+ */
+public class VarResolverContext extends Context {
+
+       /**
+        * An explicit list of Java classes to be excluded from consideration 
as being beans (<code>Set&lt;Class&gt;</code>).
+        * <p>
+        * Not-bean classes are typically converted to <code>Strings</code> 
during serialization even if they
+        * appear to be bean-like.
+        */
+       public static final String SVL_vars = "Svl.vars.set";
+
+       /**
+        * Add to the list of packages whose classes should not be considered 
beans.
+        */
+       public static final String SVL_vars_add = "Svl.vars.set.add";
+
+       /**
+        * Remove from the list of packages whose classes should not be 
considered beans.
+        */
+       public static final String SVL_vars_remove = "Svl.vars.set.remove";
+
+       /**
+        * Context objects associated with the resolver 
(<code>Map$lt;String,Object&gt;</code>).
+        */
+       public static final String SVL_context = "Svl.context.map";
+
+       /**
+        * Adds a new map entry to the {@link #SVL_context} property.
+        */
+       public static final String SVL_context_put = "Svl.context.map.put";
+
+
+       // Map of Vars added through addVar() method.
+       private final Map<String,Var> stringVars;
+
+       private final Map<String,Object> contextObjects;
+
+
+       /**
+        * Constructor.
+        *
+        * @param cf The context factory to copy from.
+        */
+       public VarResolverContext(ContextFactory cf) {
+               super(cf);
+               ContextFactory.PropertyMap pm = cf.getPropertyMap("Svl");
+
+               Class<?>[] varClasses = pm.get(SVL_vars, Class[].class, new 
Class[0]);
+               Map<String,Var> m = new ConcurrentSkipListMap<String,Var>();
+               for (Class<?> c : varClasses) {
+                       if (! ClassUtils.isParentClass(Var.class, c))
+                               throw new RuntimeException("Invalid variable 
class.  Must extend from Var");
+                       try {
+                               Var v = (Var)c.newInstance();
+                               m.put(v.getName(), v);
+                       } catch (Exception e) {
+                               throw new RuntimeException(e);
+                       }
+               }
+               this.stringVars = Collections.unmodifiableMap(m);
+
+               this.contextObjects = 
Collections.unmodifiableMap(pm.getMap(SVL_context, String.class, Object.class, 
Collections.<String,Object>emptyMap()));
+       }
+
+       /**
+        * Returns an unmodifiable map of {@link Var Vars} associated with this 
context.
+        *
+        * @return A map whose keys are var names (e.g. <js>"S"</js>) and 
values are {@link Var} instances.
+        */
+       protected Map<String,Var> getVars() {
+               return stringVars;
+       }
+
+       /**
+        * Returns the context object with the specified name.
+        *
+        * @param name The name of the context object.
+        * @return The context object, or <jk>null</jk> if no context object is 
specified with that name.
+        */
+       protected Object getContextObject(String name) {
+               return contextObjects.get(name);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverSession.java 
b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverSession.java
new file mode 100644
index 0000000..5ec3797
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverSession.java
@@ -0,0 +1,298 @@
+/***************************************************************************************************************************
+ * 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.svl;
+
+import java.util.*;
+import static java.text.MessageFormat.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.io.*;
+
+import org.apache.juneau.*;
+
+/**
+ * A var resolver session that combines a {@link VarResolver} with one or more 
session objects.
+ * <p>
+ * Instances of this class are considered light-weight and fast to construct, 
use, and discard.
+ * <p>
+ * This class contains the workhorse code for var resolution.
+ * <p>
+ * Instances of this class are created through the {@link 
VarResolver#createSession()} and {@link VarResolver#createSession(Map)}
+ *     methods.
+ * <p>
+ * Instances of this class are NOT guaranteed to be thread safe.
+ *
+ * @see org.apache.juneau.svl
+ * @author James Bognar ([email protected])
+ */
+public class VarResolverSession extends Session {
+
+       private final Map<String,Object> sessionObjects;
+       private final VarResolverContext context;
+
+       /**
+        * Constructor.
+        *
+        * @param context The {@link VarResolver} context object that contains 
the {@link Var Vars} and
+        *      context objects associated with that resolver.
+        * @param sessionObjects
+        */
+       public VarResolverSession(VarResolverContext context, 
Map<String,Object> sessionObjects) {
+               super(context);
+               this.context = context;
+               this.sessionObjects = (sessionObjects == null ? new 
TreeMap<String,Object>() : sessionObjects);
+       }
+
+       /**
+        * Adds a session object to this session.
+        *
+        * @param name The name of the session object.
+        * @param o The session object.
+        * @return This method (for method chaining).
+        */
+       public VarResolverSession setSessionObject(String name, Object o) {
+               this.sessionObjects.put(name, o);
+               return this;
+       }
+
+       /**
+        * Resolve all variables in the specified string.
+        *
+        * @param s The string to resolve variables in.
+        * @return The new string with all variables resolved, or the same 
string if no variables were found.
+        *      Null input results in a blank string.
+        */
+       public String resolve(String s) {
+
+               if (s == null)
+                       return "";
+               if (s.indexOf('$') == -1 && s.indexOf('\\') == -1)
+                       return s;
+
+               // Special case where value consists of a single variable with 
no embedded variables (e.g. "$X{...}").
+               // This is a common case, so we want an optimized solution that 
doesn't involve string builders.
+               if (isSimpleVar(s)) {
+                       String var = s.substring(1, s.indexOf('{'));
+                       String val = s.substring(s.indexOf('{')+1, 
s.length()-1);
+                       Var v = getVar(var);
+                       if (v != null) {
+                               if (v.streamed) {
+                                       StringWriter sw = new StringWriter();
+                                       v.resolveTo(this, sw, val);
+                                       return sw.toString();
+                               }
+                               s = v.doResolve(this, val);
+                               if (s == null)
+                                       s = "";
+                               return resolve(s);
+                       }
+                       return s;
+               }
+
+               try {
+                       return resolveTo(s, new StringWriter()).toString();
+               } catch (IOException e) {
+                       throw new RuntimeException(e); // Never happens.
+               }
+       }
+
+       /**
+        * Checks to see if string is of the simple form "$X{...}" with no 
embedded variables.
+        * This is a common case, and we can avoid using StringWriters.
+        */
+       private boolean isSimpleVar(String s) {
+               int S1 = 1;        // Not in variable, looking for $
+               int S2 = 2;    // Found $, Looking for {
+               int S3 = 3;    // Found {, Looking for }
+               int S4 = 4;    // Found }
+
+               int length = s.length();
+               int state = S1;
+               for (int i = 0; i < length; i++) {
+                       char c = s.charAt(i);
+                       if (state == S1) {
+                               if (c == '$') {
+                                       state = S2;
+                               } else {
+                                       return false;
+                               }
+                       } else if (state == S2) {
+                               if (c == '{') {
+                                       state = S3;
+                               } else if (c < 'A' || c > 'z' || (c > 'Z' && c 
< 'a')) {   // False trigger "$X "
+                                       return false;
+                               }
+                       } else if (state == S3) {
+                               if (c == '}')
+                                       state = S4;
+                               else if (c == '{' || c == '$')
+                                       return false;
+                       } else if (state == S4) {
+                               return false;
+                       }
+               }
+               return state == S4;
+       }
+
+       /**
+        * Resolves variables in the specified string and sends the output to 
the specified writer.
+        * More efficient than first parsing to a string and then serializing 
to the writer since this
+        * method doesn't need to construct a large string.
+        *
+        * @param s The string to resolve variables in.
+        * @param out The writer to write to.
+        * @return The same writer.
+        * @throws IOException
+        */
+       public Writer resolveTo(String s, Writer out) throws IOException {
+
+               int S1 = 1;        // Not in variable, looking for $
+               int S2 = 2;    // Found $, Looking for {
+               int S3 = 3;    // Found {, Looking for }
+
+               int state = S1;
+               boolean isInEscape = false;
+               boolean hasInternalVar = false;
+               boolean hasInnerEscapes = false;
+               String varType = null;
+               String varVal = null;
+               int x = 0, x2 = 0;
+               int depth = 0;
+               int length = s.length();
+               for (int i = 0; i < length; i++) {
+                       char c = s.charAt(i);
+                       if (state == S1) {
+                               if (isInEscape) {
+                                       if (c == '\\' || c == '$') {
+                                               out.append(c);
+                                       } else {
+                                               out.append('\\').append(c);
+                                       }
+                                       isInEscape = false;
+                               } else if (c == '\\') {
+                                       isInEscape = true;
+                               } else if (c == '$') {
+                                       x = i;
+                                       x2 = i;
+                                       state = S2;
+                               } else {
+                                       out.append(c);
+                               }
+                       } else if (state == S2) {
+                               if (isInEscape) {
+                                       isInEscape = false;
+                               } else if (c == '\\') {
+                                       hasInnerEscapes = true;
+                                       isInEscape = true;
+                               } else if (c == '{') {
+                                       varType = s.substring(x+1, i);
+                                       x = i;
+                                       state = S3;
+                               } else if (c < 'A' || c > 'z' || (c > 'Z' && c 
< 'a')) {  // False trigger "$X "
+                                       if (hasInnerEscapes)
+                                               
out.append(unEscapeChars(s.substring(x, i+1), new char[]{'\\','{'}));
+                                       else
+                                               out.append(s, x, i+1);
+                                       x = i + 1;
+                                       state = S1;
+                                       hasInnerEscapes = false;
+                               }
+                       } else if (state == S3) {
+                               if (isInEscape) {
+                                       isInEscape = false;
+                               } else if (c == '\\') {
+                                       isInEscape = true;
+                                       hasInnerEscapes = true;
+                               } else if (c == '{') {
+                                       depth++;
+                                       hasInternalVar = true;
+                               } else if (c == '}') {
+                                       if (depth > 0) {
+                                               depth--;
+                                       } else {
+                                               varVal = s.substring(x+1, i);
+                                               varVal = (hasInternalVar ? 
resolve(varVal) : varVal);
+                                               Var r = getVar(varType);
+                                               if (r == null) {
+                                                       if (hasInnerEscapes)
+                                                               
out.append(unEscapeChars(s.substring(x2, i+1), new char[]{'\\','$','{','}'}));
+                                                       else
+                                                               out.append(s, 
x2, i+1);
+                                                       x = i+1;
+                                               } else {
+                                                       if (r.streamed)
+                                                               
r.resolveTo(this, out, varVal);
+                                                       else {
+                                                               String 
replacement = r.doResolve(this, varVal);
+                                                               if (replacement 
== null)
+                                                                       
replacement = "";
+                                                               // If the 
replacement also contains variables, replace them now.
+                                                               if 
(replacement.indexOf('$') != -1)
+                                                                       
replacement = resolve(replacement);
+                                                               
out.append(replacement);
+                                                       }
+                                                       x = i+1;
+                                               }
+                                               state = 1;
+                                               hasInnerEscapes = false;
+                                       }
+                               }
+                       }
+               }
+               if (isInEscape)
+                       out.append('\\');
+               else if (state == S2)
+                       out.append('$').append(unEscapeChars(s.substring(x+1), 
new char[]{'{', '\\'}));
+               else if (state == S3)
+                       
out.append('$').append(varType).append('{').append(unEscapeChars(s.substring(x+1),
 new char[]{'\\','$','{','}'}));
+               return out;
+       }
+
+
+       /**
+        * Returns the session object with the specified name.
+        * Casts it to the specified class type for you.
+        *
+        * @param c The class type to cast to.
+        * @param name The name of the session object.
+        * @return The session object.  Never <jk>null</jk>.
+        * @throws RuntimeException If session object with specified name does 
not exist.
+        */
+       @SuppressWarnings("unchecked")
+       public <T> T getSessionObject(Class<T> c, String name) {
+               T t = null;
+               try {
+                       t = (T)sessionObjects.get(name);
+                       if (t == null) {
+                               sessionObjects.put(name, 
this.context.getContextObject(name));
+                               t = (T)sessionObjects.get(name);
+                       }
+               } catch (Exception e) {
+                       throw new RuntimeException(format("Session object 
''{0}'' or context object ''SvlContext.{0}'' could not be converted to type 
''{1}''.", name, c.getName()));
+               }
+               if (t == null)
+                       throw new RuntimeException(format("Session object 
''{0}'' or context object ''SvlContext.{0}'' not found.", name));
+               return t;
+       }
+
+       /**
+        * Returns the {@link Var} with the specified name.
+        *
+        * @param name The var name (e.g. <js>"S"</js>).
+        * @return The {@link Var} instance, or <jk>null</jk> if no 
<code>Var</code> is associated with the specified name.
+        */
+       protected Var getVar(String name) {
+               return this.context.getVars().get(name);
+       }
+}

Reply via email to