001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.serializer;
014
015import static org.apache.juneau.internal.ClassUtils.*;
016import static org.apache.juneau.internal.StringUtils.*;
017
018import java.io.*;
019import java.lang.reflect.*;
020import java.text.*;
021import java.util.*;
022
023import org.apache.juneau.*;
024import org.apache.juneau.internal.*;
025import org.apache.juneau.parser.*;
026import org.apache.juneau.soap.*;
027import org.apache.juneau.transform.*;
028
029/**
030 * Serializer session that lives for the duration of a single use of {@link Serializer}.
031 *
032 * <p>
033 * Used by serializers for the following purposes:
034 * <ul class='spaced-list'>
035 *    <li>
036 *       Keeping track of how deep it is in a model for indentation purposes.
037 *    <li>
038 *       Ensuring infinite loops don't occur by setting a limit on how deep to traverse a model.
039 *    <li>
040 *       Ensuring infinite loops don't occur from loops in the model (when detectRecursions is enabled.
041 *    <li>
042 *       Allowing serializer properties to be overridden on method calls.
043 * </ul>
044 *
045 * <p>
046 * This class is NOT thread safe.
047 * It is typically discarded after one-time use although it can be reused within the same thread.
048 */
049public abstract class SerializerSession extends BeanTraverseSession {
050
051   private final Serializer ctx;
052   private final UriResolver uriResolver;
053
054   private final Method javaMethod;                                                // Java method that invoked this serializer.
055
056   // Writable properties
057   private final boolean useWhitespace;
058   private final SerializerListener listener;
059
060   /**
061    * Create a new session using properties specified in the context.
062    *
063    * @param ctx
064    *    The context creating this session object.
065    *    The context contains all the configuration settings for this object.
066    *    Can be <jk>null</jk>.
067    * @param args
068    *    Runtime arguments.
069    *    These specify session-level information such as locale and URI context.
070    *    It also include session-level properties that override the properties defined on the bean and
071    *    serializer contexts.
072    */
073   protected SerializerSession(Serializer ctx, SerializerSessionArgs args) {
074      super(ctx, args == null ? SerializerSessionArgs.DEFAULT : args);
075      this.ctx = ctx;
076      args = args == null ? SerializerSessionArgs.DEFAULT : args;
077      this.javaMethod = args.javaMethod;
078      this.uriResolver = new UriResolver(ctx.getUriResolution(), ctx.getUriRelativity(), args.uriContext == null ? ctx.getUriContext() : args.uriContext);
079      this.listener = newInstance(SerializerListener.class, ctx.getListener());
080      this.useWhitespace = args.useWhitespace != null ? args.useWhitespace : ctx.isUseWhitespace();
081   }
082
083   /**
084    * Default constructor.
085    *
086    * @param args
087    *    Runtime arguments.
088    *    These specify session-level information such as locale and URI context.
089    *    It also include session-level properties that override the properties defined on the bean and
090    *    serializer contexts.
091    */
092   protected SerializerSession(SerializerSessionArgs args) {
093      this(Serializer.DEFAULT, args);
094   }
095
096   @Override /* Session */
097   public ObjectMap asMap() {
098      return super.asMap()
099         .append("SerializerSession", new ObjectMap()
100            .append("uriResolver", uriResolver)
101         );
102   }
103
104   /**
105    * Wraps the specified input object into a {@link ParserPipe} object so that it can be easily converted into
106    * a stream or reader.
107    *
108    * @param output
109    *    The output location.
110    *    <br>For character-based serializers, this can be any of the following types:
111    *    <ul>
112    *       <li>{@link Writer}
113    *       <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
114    *       <li>{@link File} - Output will be written as system-default encoded stream.
115    *       <li>{@link StringBuilder}
116    *    </ul>
117    *    <br>For byte-based serializers, this can be any of the following types:
118    *    <ul>
119    *       <li>{@link OutputStream}
120    *       <li>{@link File}
121    *    </ul>
122    * @return
123    *    A new {@link ParserPipe} wrapper around the specified input object.
124    */
125   protected SerializerPipe createPipe(Object output) {
126      return new SerializerPipe(output);
127   }
128
129
130   //-----------------------------------------------------------------------------------------------------------------
131   // Abstract methods
132   //-----------------------------------------------------------------------------------------------------------------
133
134   /**
135    * Serializes a POJO to the specified output stream or writer.
136    *
137    * <p>
138    * This method should NOT close the context object.
139    *
140    * @param pipe Where to send the output from the serializer.
141    * @param o The object to serialize.
142    * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed.
143    */
144   protected abstract void doSerialize(SerializerPipe pipe, Object o) throws Exception;
145
146   /**
147    * Shortcut method for serializing objects directly to either a <code>String</code> or <code><jk>byte</jk>[]</code>
148    * depending on the serializer type.
149    *
150    * @param o The object to serialize.
151    * @return
152    *    The serialized object.
153    *    <br>Character-based serializers will return a <code>String</code>.
154    *    <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code>.
155    * @throws SerializeException If a problem occurred trying to convert the output.
156    */
157   public abstract Object serialize(Object o) throws SerializeException;
158
159   /**
160    * Shortcut method for serializing an object to a String.
161    *
162    * @param o The object to serialize.
163    * @return
164    *    The serialized object.
165    *    <br>Character-based serializers will return a <code>String</code>
166    *    <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code> converted to a string based on the {@link OutputStreamSerializer#OSSERIALIZER_binaryFormat} setting.
167    * @throws SerializeException If a problem occurred trying to convert the output.
168    */
169   public abstract String serializeToString(Object o) throws SerializeException;
170
171   /**
172    * Returns <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}.
173    *
174    * @return <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}.
175    */
176   public abstract boolean isWriterSerializer();
177
178
179   //-----------------------------------------------------------------------------------------------------------------
180   // Other methods
181   //-----------------------------------------------------------------------------------------------------------------
182
183   /**
184    * Serialize the specified object using the specified session.
185    *
186    * @param out Where to send the output from the serializer.
187    * @param o The object to serialize.
188    * @throws SerializeException If a problem occurred trying to convert the output.
189    */
190   public final void serialize(Object o, Object out) throws SerializeException {
191      try (SerializerPipe pipe = createPipe(out)) {
192         doSerialize(pipe, o);
193      } catch (SerializeException e) {
194         throw e;
195      } catch (StackOverflowError e) {
196         throw new SerializeException(this,
197            "Stack overflow occurred.  This can occur when trying to serialize models containing loops.  It's recommended you use the BeanTraverseContext.BEANTRAVERSE_detectRecursions setting to help locate the loop.").initCause(e);
198      } catch (Exception e) {
199         throw new SerializeException(this, e);
200      } finally {
201         checkForWarnings();
202      }
203   }
204
205   /**
206    * Returns the Java method that invoked this serializer.
207    *
208    * <p>
209    * When using the REST API, this is the Java method invoked by the REST call.
210    * Can be used to access annotations defined on the method or class.
211    *
212    * @return The Java method that invoked this serializer.
213   */
214   protected final Method getJavaMethod() {
215      return javaMethod;
216   }
217
218   /**
219    * Returns the URI resolver.
220    *
221    * @return The URI resolver.
222    */
223   protected final UriResolver getUriResolver() {
224      return uriResolver;
225   }
226
227   /**
228    * Specialized warning when an exception is thrown while executing a bean getter.
229    *
230    * @param p The bean map entry representing the bean property.
231    * @param t The throwable that the bean getter threw.
232    */
233   protected final void onBeanGetterException(BeanPropertyMeta p, Throwable t) {
234      if (listener != null)
235         listener.onBeanGetterException(this, t, p);
236      String prefix = (isDebug() ? getStack(false) + ": " : "");
237      addWarning("{0}Could not call getValue() on property ''{1}'' of class ''{2}'', exception = {3}", prefix,
238         p.getName(), p.getBeanMeta().getClassMeta(), t.getLocalizedMessage());
239   }
240
241   /**
242    * Logs a warning message.
243    *
244    * @param t The throwable that was thrown (if there was one).
245    * @param msg The warning message.
246    * @param args Optional {@link MessageFormat}-style arguments.
247    */
248   @Override
249   protected void onError(Throwable t, String msg, Object... args) {
250      if (listener != null)
251         listener.onError(this, t, format(msg, args));
252      super.onError(t, msg, args);
253   }
254
255   /**
256    * Trims the specified string if {@link SerializerSession#isTrimStrings()} returns <jk>true</jk>.
257    *
258    * @param o The input string to trim.
259    * @return The trimmed string, or <jk>null</jk> if the input was <jk>null</jk>.
260    */
261   public final String trim(Object o) {
262      if (o == null)
263         return null;
264      String s = o.toString();
265      if (isTrimStrings())
266         s = s.trim();
267      return s;
268   }
269
270   /**
271    * Generalize the specified object if a POJO swap is associated with it.
272    *
273    * @param o The object to generalize.
274    * @param type The type of object.
275    * @return The generalized object, or <jk>null</jk> if the object is <jk>null</jk>.
276    * @throws SerializeException If a problem occurred trying to convert the output.
277    */
278   @SuppressWarnings({ "rawtypes", "unchecked" })
279   protected final Object generalize(Object o, ClassMeta<?> type) throws SerializeException {
280      try {
281         if (o == null)
282            return null;
283         PojoSwap f = (type == null || type.isObject() ? getClassMeta(o.getClass()).getPojoSwap(this) : type.getPojoSwap(this));
284         if (f == null)
285            return o;
286         return f.swap(this, o);
287      } catch (SerializeException e) {
288         throw e;
289      } catch (Exception e) {
290         throw new SerializeException(e);
291      }
292   }
293
294   /**
295    * Returns <jk>true</jk> if the specified value should not be serialized.
296    *
297    * @param cm The class type of the object being serialized.
298    * @param attrName The bean attribute name, or <jk>null</jk> if this isn't a bean attribute.
299    * @param value The object being serialized.
300    * @return <jk>true</jk> if the specified value should not be serialized.
301    * @throws SerializeException If recursion occurred.
302    */
303   public final boolean canIgnoreValue(ClassMeta<?> cm, String attrName, Object value) throws SerializeException {
304
305      if (isTrimNullProperties() && value == null)
306         return true;
307
308      if (value == null)
309         return false;
310
311      if (cm == null)
312         cm = object();
313
314      if (isTrimEmptyCollections()) {
315         if (cm.isArray() || (cm.isObject() && value.getClass().isArray())) {
316            if (((Object[])value).length == 0)
317               return true;
318         }
319         if (cm.isCollection() || (cm.isObject() && isParentClass(Collection.class, value.getClass()))) {
320            if (((Collection<?>)value).isEmpty())
321               return true;
322         }
323      }
324
325      if (isTrimEmptyMaps()) {
326         if (cm.isMap() || (cm.isObject() && isParentClass(Map.class, value.getClass()))) {
327            if (((Map<?,?>)value).isEmpty())
328               return true;
329         }
330      }
331
332      try {
333         if (isTrimNullProperties() && willRecurse(attrName, value, cm))
334            return true;
335      } catch (BeanRecursionException e) {
336         throw new SerializeException(e);
337      }
338
339      return false;
340   }
341
342   /**
343    * Sorts the specified map if {@link SerializerSession#isSortMaps()} returns <jk>true</jk>.
344    *
345    * @param m The map being sorted.
346    * @return A new sorted {@link TreeMap}.
347    */
348   public final <K,V> Map<K,V> sort(Map<K,V> m) {
349      if (isSortMaps() && m != null && (! m.isEmpty()) && m.keySet().iterator().next() instanceof Comparable<?>)
350         return new TreeMap<>(m);
351      return m;
352   }
353
354   /**
355    * Sorts the specified collection if {@link SerializerSession#isSortCollections()} returns <jk>true</jk>.
356    *
357    * @param c The collection being sorted.
358    * @return A new sorted {@link TreeSet}.
359    */
360   public final <E> Collection<E> sort(Collection<E> c) {
361      if (isSortCollections() && c != null && (! c.isEmpty()) && c.iterator().next() instanceof Comparable<?>)
362         return new TreeSet<>(c);
363      return c;
364   }
365
366   /**
367    * Converts the contents of the specified object array to a list.
368    *
369    * <p>
370    * Works on both object and primitive arrays.
371    *
372    * <p>
373    * In the case of multi-dimensional arrays, the outgoing list will contain elements of type n-1 dimension.
374    * i.e. if {@code type} is <code><jk>int</jk>[][]</code> then {@code list} will have entries of type
375    * <code><jk>int</jk>[]</code>.
376    *
377    * @param type The type of array.
378    * @param array The array being converted.
379    * @return The array as a list.
380    */
381   protected static final List<Object> toList(Class<?> type, Object array) {
382      Class<?> componentType = type.getComponentType();
383      if (componentType.isPrimitive()) {
384         int l = Array.getLength(array);
385         List<Object> list = new ArrayList<>(l);
386         for (int i = 0; i < l; i++)
387            list.add(Array.get(array, i));
388         return list;
389      }
390      return Arrays.asList((Object[])array);
391   }
392
393   /**
394    * Converts a String to an absolute URI based on the {@link UriContext} on this session.
395    *
396    * @param uri
397    *    The input URI.
398    *    Can be any of the following:
399    *    <ul>
400    *       <li>{@link java.net.URI}
401    *       <li>{@link java.net.URL}
402    *       <li>{@link CharSequence}
403    *    </ul>
404    *    URI can be any of the following forms:
405    *    <ul>
406    *       <li><js>"foo://foo"</js> - Absolute URI.
407    *       <li><js>"/foo"</js> - Root-relative URI.
408    *       <li><js>"/"</js> - Root URI.
409    *       <li><js>"context:/foo"</js> - Context-root-relative URI.
410    *       <li><js>"context:/"</js> - Context-root URI.
411    *       <li><js>"servlet:/foo"</js> - Servlet-path-relative URI.
412    *       <li><js>"servlet:/"</js> - Servlet-path URI.
413    *       <li><js>"request:/foo"</js> - Request-path-relative URI.
414    *       <li><js>"request:/"</js> - Request-path URI.
415    *       <li><js>"foo"</js> - Path-info-relative URI.
416    *       <li><js>""</js> - Path-info URI.
417    *    </ul>
418    * @return The resolved URI.
419    */
420   public final String resolveUri(Object uri) {
421      return uriResolver.resolve(uri);
422   }
423
424   /**
425    * Opposite of {@link #resolveUri(Object)}.
426    *
427    * <p>
428    * Converts the URI to a value relative to the specified <code>relativeTo</code> parameter.
429    *
430    * <p>
431    * Both parameters can be any of the following:
432    * <ul>
433    *    <li>{@link java.net.URI}
434    *    <li>{@link java.net.URL}
435    *    <li>{@link CharSequence}
436    * </ul>
437    *
438    * <p>
439    * Both URIs can be any of the following forms:
440    * <ul>
441    *    <li><js>"foo://foo"</js> - Absolute URI.
442    *    <li><js>"/foo"</js> - Root-relative URI.
443    *    <li><js>"/"</js> - Root URI.
444    *    <li><js>"context:/foo"</js> - Context-root-relative URI.
445    *    <li><js>"context:/"</js> - Context-root URI.
446    *    <li><js>"servlet:/foo"</js> - Servlet-path-relative URI.
447    *    <li><js>"servlet:/"</js> - Servlet-path URI.
448    *    <li><js>"request:/foo"</js> - Request-path-relative URI.
449    *    <li><js>"request:/"</js> - Request-path URI.
450    *    <li><js>"foo"</js> - Path-info-relative URI.
451    *    <li><js>""</js> - Path-info URI.
452    * </ul>
453    *
454    * @param relativeTo The URI to relativize against.
455    * @param uri The URI to relativize.
456    * @return The relativized URI.
457    */
458   protected final String relativizeUri(Object relativeTo, Object uri) {
459      return uriResolver.relativize(relativeTo, uri);
460   }
461
462   /**
463    * Converts the specified object to a <code>String</code>.
464    *
465    * <p>
466    * Also has the following effects:
467    * <ul>
468    *    <li><code>Class</code> object is converted to a readable name.  See {@link ClassUtils#getReadableClassName(Class)}.
469    *    <li>Whitespace is trimmed if the trim-strings setting is enabled.
470    * </ul>
471    *
472    * @param o The object to convert to a <code>String</code>.
473    * @return The object converted to a String, or <jk>null</jk> if the input was <jk>null</jk>.
474    */
475   public final String toString(Object o) {
476      if (o == null)
477         return null;
478      if (o.getClass() == Class.class)
479         return getReadableClassName((Class<?>)o);
480      if (o.getClass().isEnum())
481         return getClassMetaForObject(o).toString(o);
482      String s = o.toString();
483      if (isTrimStrings())
484         s = s.trim();
485      return s;
486   }
487
488   /**
489    * Create a "_type" property that contains the dictionary name of the bean.
490    *
491    * @param m The bean map to create a class property on.
492    * @param typeName The type name of the bean.
493    * @return A new bean property value.
494    */
495   protected static final BeanPropertyValue createBeanTypeNameProperty(BeanMap<?> m, String typeName) {
496      BeanMeta<?> bm = m.getMeta();
497      return new BeanPropertyValue(bm.getTypeProperty(), bm.getTypeProperty().getName(), typeName, null);
498   }
499
500   /**
501    * Resolves the dictionary name for the actual type.
502    *
503    * @param eType The expected type of the bean property.
504    * @param aType The actual type of the bean property.
505    * @param pMeta The current bean property being serialized.
506    * @return The bean dictionary name, or <jk>null</jk> if a name could not be found.
507    */
508   protected final String getBeanTypeName(ClassMeta<?> eType, ClassMeta<?> aType, BeanPropertyMeta pMeta) {
509      if (eType == aType)
510         return null;
511
512      if (! isAddBeanTypes())
513         return null;
514
515      String eTypeTn = eType.getDictionaryName();
516
517      // First see if it's defined on the actual type.
518      String tn = aType.getDictionaryName();
519      if (tn != null && ! tn.equals(eTypeTn)) {
520         return tn;
521      }
522
523      // Then see if it's defined on the expected type.
524      // The expected type might be an interface with mappings for implementation classes.
525      BeanRegistry br = eType.getBeanRegistry();
526      if (br != null) {
527         tn = br.getTypeName(aType);
528         if (tn != null && ! tn.equals(eTypeTn))
529            return tn;
530      }
531
532      // Then look on the bean property.
533      br = pMeta == null ? null : pMeta.getBeanRegistry();
534      if (br != null) {
535         tn = br.getTypeName(aType);
536         if (tn != null && ! tn.equals(eTypeTn))
537            return tn;
538      }
539
540      // Finally look in the session.
541      br = getBeanRegistry();
542      if (br != null) {
543         tn = br.getTypeName(aType);
544         if (tn != null && ! tn.equals(eTypeTn))
545            return tn;
546      }
547
548      return null;
549   }
550
551   /**
552    * Returns the parser-side expected type for the object.
553    *
554    * <p>
555    * The return value depends on the {@link Serializer#SERIALIZER_addRootType} setting.
556    * When disabled, the parser already knows the Java POJO type being parsed, so there is
557    * no reason to add <js>"_type"</js> attributes to the root-level object.
558    *
559    * @param o The object to get the expected type on.
560    * @return The expected type.
561    */
562   protected final ClassMeta<?> getExpectedRootType(Object o) {
563      return isAddRootType() ? object() : getClassMetaForObject(o);
564   }
565
566   /**
567    * Optional method that specifies HTTP request headers for this serializer.
568    *
569    * <p>
570    * For example, {@link SoapXmlSerializer} needs to set a <code>SOAPAction</code> header.
571    *
572    * <p>
573    * This method is typically meaningless if the serializer is being used stand-alone (i.e. outside of a REST server
574    * or client).
575    *
576    * @return
577    *    The HTTP headers to set on HTTP requests.
578    *    Never <jk>null</jk>.
579    */
580   public Map<String,String> getResponseHeaders() {
581      return Collections.emptyMap();
582   }
583
584
585   /**
586    * Returns the listener associated with this session.
587    *
588    * @return The listener associated with this session, or <jk>null</jk> if there is no listener.
589    */
590   public SerializerListener getListener() {
591      return listener;
592   }
593
594   /**
595    * Returns the listener associated with this session.
596    *
597    * @param c The listener class to cast to.
598    * @return The listener associated with this session, or <jk>null</jk> if there is no listener.
599    */
600   @SuppressWarnings("unchecked")
601   public <T extends SerializerListener> T getListener(Class<T> c) {
602      return (T)listener;
603   }
604
605   //-----------------------------------------------------------------------------------------------------------------
606   // Properties
607   //-----------------------------------------------------------------------------------------------------------------
608
609   /**
610    * Configuration property:  Use whitespace.
611    *
612    * @see Serializer#SERIALIZER_useWhitespace
613    * @return
614    *    <jk>true</jk> if whitespace is added to the output to improve readability.
615    */
616   // TODO - Make final in 8.0
617   public boolean isUseWhitespace() {
618      return useWhitespace;
619   }
620
621   /**
622    * Configuration property:  Add <js>"_type"</js> properties when needed.
623    *
624    * @see Serializer#SERIALIZER_addBeanTypes
625    * @return
626    *    <jk>true</jk> if <js>"_type"</js> properties added to beans if their type cannot be inferred
627    *    through reflection.
628    */
629   protected boolean isAddBeanTypes() {
630      return ctx.isAddBeanTypes();
631   }
632
633   /**
634    * Configuration property:  Trim null bean property values.
635    *
636    * @see Serializer#SERIALIZER_trimNullProperties
637    * @return
638    *    <jk>true</jk> if null bean values are not serialized to the output.
639    */
640   protected final boolean isTrimNullProperties() {
641      return ctx.isTrimNullProperties();
642   }
643
644   /**
645    * Configuration property:  Trim empty lists and arrays.
646    *
647    * @see Serializer#SERIALIZER_trimEmptyCollections
648    * @return
649    *    <jk>true</jk> if empty lists and arrays are not serialized to the output.
650    */
651   protected final boolean isTrimEmptyCollections() {
652      return ctx.isTrimEmptyCollections();
653   }
654
655   /**
656    * Configuration property:  Trim empty maps.
657    *
658    * @see Serializer#SERIALIZER_trimEmptyMaps
659    * @return
660    *    <jk>true</jk> if empty map values are not serialized to the output.
661    */
662   protected final boolean isTrimEmptyMaps() {
663      return ctx.isTrimEmptyMaps();
664   }
665
666   /**
667    * Configuration property:  Trim strings.
668    *
669    * @see Serializer#SERIALIZER_trimStrings
670    * @return
671    *    <jk>true</jk> if string values will be trimmed of whitespace using {@link String#trim()} before being serialized.
672    */
673   protected boolean isTrimStrings() {
674      return ctx.isTrimStrings();
675   }
676
677   /**
678    * Configuration property:  Sort arrays and collections alphabetically.
679    *
680    * @see Serializer#SERIALIZER_sortCollections
681    * @return
682    *    <jk>true</jk> if arrays and collections are copied and sorted before serialization.
683    */
684   protected final boolean isSortCollections() {
685      return ctx.isSortCollections();
686   }
687
688   /**
689    * Configuration property:  Sort maps alphabetically.
690    *
691    * @see Serializer#SERIALIZER_sortMaps
692    * @return
693    *    <jk>true</jk> if maps are copied and sorted before serialization.
694    */
695   protected final boolean isSortMaps() {
696      return ctx.isSortMaps();
697   }
698
699   /**
700    * Configuration property:  Add type attribute to root nodes.
701    *
702    * @see Serializer#SERIALIZER_addRootType
703    * @return
704    *    <jk>true</jk> if type property should be added to root node.
705    */
706   protected final boolean isAddRootType() {
707      return ctx.isAddRootType();
708   }
709
710   /**
711    * Configuration property:  URI context bean.
712    *
713    * @see Serializer#SERIALIZER_uriContext
714    * @return
715    *    Bean used for resolution of URIs to absolute or root-relative form.
716    */
717   protected final UriContext getUriContext() {
718      return getUriContext();
719   }
720
721   /**
722    * Configuration property:  URI resolution.
723    *
724    * @see Serializer#SERIALIZER_uriResolution
725    * @return
726    *    Defines the resolution level for URIs when serializing URIs.
727    */
728   protected final UriResolution getUriResolution() {
729      return ctx.getUriResolution();
730   }
731
732   /**
733    * Configuration property:  URI relativity.
734    *
735    * @see Serializer#SERIALIZER_uriRelativity
736    * @return
737    *    Defines what relative URIs are relative to when serializing any of the following:
738    */
739   protected final UriRelativity getUriRelativity() {
740      return ctx.getUriRelativity();
741   }
742}