package com.fasterxml.aalto.out;
import java.util.*;
import java.lang.ref.
SoftReference;
import javax.xml.stream.
XMLOutputFactory; // to get constants
import org.codehaus.stax2.
XMLOutputFactory2;
import org.codehaus.stax2.
XMLStreamProperties;
import com.fasterxml.aalto.impl.
CommonConfig;
import com.fasterxml.aalto.util.
BufferRecycler;
/**
* This is the shared configuration object passed by the factory to writer.
*/
public final class
WriterConfig
extends
CommonConfig
{
// // // Constants for defaults
protected final static
String DEFAULT_AUTOMATIC_NS_PREFIX = "ans";
// Standard Stax flags:
final static int
F_NS_REPAIRING = 0x0001;
// Standard Stax2 flags:
final static int
F_AUTO_CLOSE_OUTPUT = 0x0010;
final static int
F_NS_AWARE = 0x0020;
final static int
F_AUTO_EMPTY_ELEMS = 0x0040;
// Non-flag (object) properties
final static int
PROP_AUTO_NS_PREFIX = -2;
// No flags are set by default, yet...
final static int
DEFAULT_FLAGS =
F_NS_AWARE
;
private final static
HashMap<
String,
Integer>
sProperties;
static {
sProperties = new
HashMap<
String,
Integer>();
sProperties.
put(
XMLOutputFactory.
IS_REPAIRING_NAMESPACES,
Integer.
valueOf(
F_NS_REPAIRING));
// Stax2:
// not configurable, but are recognized
sProperties.
put(
XMLStreamProperties.
XSP_NAMESPACE_AWARE,
Integer.
valueOf(
F_NS_AWARE));
sProperties.
put(
XMLStreamProperties.
XSP_PROBLEM_REPORTER, null);
// and then writer-side properties, mostly unsupported but recognized
sProperties.
put(
XMLOutputFactory2.
P_AUTO_CLOSE_OUTPUT,
Integer.
valueOf(
F_AUTO_CLOSE_OUTPUT));
sProperties.
put(
XMLOutputFactory2.
P_AUTOMATIC_EMPTY_ELEMENTS,
Integer.
valueOf(
F_AUTO_EMPTY_ELEMS));
sProperties.
put(
XMLOutputFactory2.
P_AUTOMATIC_NS_PREFIX,
Integer.
valueOf(
PROP_AUTO_NS_PREFIX));
sProperties.
put(
XMLOutputFactory2.
P_TEXT_ESCAPER, null);
sProperties.
put(
XMLOutputFactory2.
P_ATTR_VALUE_ESCAPER, null);
}
/*
/**********************************************************************
/* Configurable object properties:
/**********************************************************************
*/
private
String _propAutoNsPrefix;
/*
/**********************************************************************
/* Output/stream state:
/**********************************************************************
*/
/**
* A single encoding context instance is shared between all WriterConfig
* instances created for readers by an output factory. It is used
* for sharing symbol tables.
*/
private final
EncodingContext _encodingContext;
/**
* Encoding passed in either during construction, or via xml
* declaration write.
*/
private
String _encoding;
/*
/**********************************************************************
/* Buffer recycling:
/**********************************************************************
*/
/**
* This <code>ThreadLocal</code> contains a {@link SoftRerefence}
* to a {@link BufferRecycler} used to provide a low-cost
* buffer recycling between Reader instances.
*/
final static
ThreadLocal<
SoftReference<
BufferRecycler>>
mRecyclerRef =
new
ThreadLocal<
SoftReference<
BufferRecycler>>();
/**
* This is the actually container of the recyclable buffers. It
* is obtained via ThreadLocal/SoftReference combination, if one
* exists, when Config instance is created. If one does not
* exist, it will created first time a buffer is returned.
*/
BufferRecycler _currRecycler = null;
/*
/**********************************************************************
/* Life-cycle
/**********************************************************************
*/
private
WriterConfig(
String encoding, int
flags, int
flagMods,
EncodingContext encCtxt,
String autoNsPrefix)
{
super(
flags,
flagMods);
_encoding =
encoding;
_encodingContext =
encCtxt;
/* Ok, let's then see if we can find a buffer recycler. Since they
* are lazily constructed, and since GC may just flush them out
* on its whims, it's possible we might not find one. That's ok;
* we can reconstruct one if and when we are to return one or more
* buffers.
*/
SoftReference<
BufferRecycler>
ref =
mRecyclerRef.
get();
if (
ref != null) {
_currRecycler =
ref.
get();
}
_flags =
flags;
_flagMods =
flagMods;
_propAutoNsPrefix =
autoNsPrefix;
}
public
WriterConfig()
{
this(null,
DEFAULT_FLAGS, 0, new
EncodingContext(),
DEFAULT_AUTOMATIC_NS_PREFIX);
}
public void
setActualEncodingIfNotSet(
String enc)
{
if (
_encoding == null ||
_encoding.
length() == 0) {
_encoding =
enc;
}
}
public void
doAutoCloseOutput(boolean
state) {
setFlag(
F_AUTO_CLOSE_OUTPUT,
state);
}
public void
enableXml11()
{
// !!! TBI
}
/*
/**********************************************************************
/* Common accessors from CommonConfig
/**********************************************************************
*/
public
WriterConfig createNonShared()
{
return new
WriterConfig(
_encoding,
_flags,
_flagMods,
_encodingContext,
_propAutoNsPrefix);
}
@
Override
public boolean
isXml11() {
return false;
}
@
Override
public
String getExternalEncoding()
{
/* !!! 01-Jan-2007, tatus: Can we distinguish this from the
* actual encoding? Should we be able to?
*/
return
getActualEncoding();
}
@
Override
public
String getActualEncoding() { return
_encoding; }
public
String getPreferredEncoding()
{
/* If we did have 2 separate encoding decls, we'd prefer actual
* over external. For now, doesn't matter
*/
return
_encoding;
}
/*
/**********************************************************************
/* Accessors, configurable properties
/**********************************************************************
*/
@
Override
public
Object getProperty(
String name, boolean
isMandatory)
{
Integer I =
sProperties.
get(
name);
if (
I == null) {
// Might still have it though
if (
sProperties.
containsKey(
name)) {
return null;
}
return super.getProperty(
name,
isMandatory);
}
int
f =
I.
intValue();
if (
f >= 0) {
return
hasFlag(
f);
}
switch (
f) {
case
PROP_AUTO_NS_PREFIX:
return
_propAutoNsPrefix;
}
// Need to handle non numerics separately?
return null;
}
@
Override
public boolean
setProperty(
String name,
Object value)
{
Integer I =
sProperties.
get(
name);
if (
I == null) {
// Might still have it though
if (
sProperties.
containsKey(
name)) {
return false;
}
return super.setProperty(
name,
value);
}
int
f =
I.
intValue();
if (
f >= 0) { // boolean values
boolean
state = ((
Boolean)
value).
booleanValue();
// Some properties not supported:
if (
f ==
F_NS_AWARE) {
if (!
state) {
//throw new IllegalArgumentException("Can not disable namespace-support for stream writers");
return false; // not supported
}
}
setFlag(
f,
state);
return true;
}
// object values ones
switch (
f) {
case
PROP_AUTO_NS_PREFIX:
_propAutoNsPrefix =
value.
toString();
return true;
}
return false;
}
@
Override
public boolean
isPropertySupported(
String propName)
{
return
sProperties.
containsKey(
propName)
|| super.isPropertySupported(
propName);
}
/*
/**********************************************************************
/* Accessors
/**********************************************************************
*/
// // // Configuration, Stax std props:
public boolean
willRepairNamespaces() {
return
hasFlag(
F_NS_REPAIRING);
}
public boolean
isNamespaceAware() {
// !!! TBI
return
hasFlag(
F_NS_AWARE);
}
// // // Stax2 standard properties
public boolean
willAutoCloseOutput() {
return
hasFlag(
F_AUTO_CLOSE_OUTPUT);
}
/**
* @return Prefix to use as the base for automatically generated
* namespace prefixes ("namespace prefix prefix", so to speak).
* Defaults to "axns".
*/
public
String getAutomaticNsPrefix() {
return
_propAutoNsPrefix;
}
/*
/**********************************************************************
/* Stax2 additions
/**********************************************************************
*/
public void
configureForXmlConformance()
{
// !!! TBI
/*
doValidateAttributes(true);
doValidateContent(true);
doValidateStructure(true);
doValidateNames(true);
*/
}
public void
configureForRobustness()
{
// !!! TBI
/*
doValidateAttributes(true);
doValidateStructure(true);
doValidateNames(true);
doValidateContent(true);
doFixContent(true);
*/
}
/**
* For Woodstox, setting this profile disables most checks for validity;
* specifically anything that can have measurable performance impact.
*
*/
public void
configureForSpeed()
{
// !!! TBI
/*
doValidateAttributes(false);
doValidateContent(false);
doValidateNames(false);
// Structural validation is cheap: can be left enabled (if already so)
//doValidateStructure(false);
*/
}
/*
/**********************************************************************
/* Impl specific additions, validation
/**********************************************************************
*/
public boolean
willCheckStructure() {
// !!! TBI
return true;
}
public boolean
willCheckContent() {
// !!! TBI
return true;
}
public boolean
willCheckNames() {
// !!! TBI
return false;
}
public boolean
willCheckAttributes() {
// !!! TBI
return false;
}
public boolean
willFixContent() {
// !!! TBI
return true;
}
public boolean
willEscapeCR() {
// !!! TBI
return true;
}
/*
/**********************************************************************
/* Buffer recycling:
/**********************************************************************
*/
public char[]
allocSmallCBuffer(int
minSize)
{
if (
_currRecycler != null) {
char[]
result =
_currRecycler.
getSmallCBuffer(
minSize);
if (
result != null) {
return
result;
}
}
// Nope; no recycler, or it has no suitable buffers, let's create:
return new char[
minSize];
}
public void
freeSmallCBuffer(char[]
buffer)
{
// Need to create (and assign) the buffer?
if (
_currRecycler == null) {
_currRecycler =
createRecycler();
}
_currRecycler.
returnSmallCBuffer(
buffer);
}
public char[]
allocMediumCBuffer(int
minSize)
{
if (
_currRecycler != null) {
char[]
result =
_currRecycler.
getMediumCBuffer(
minSize);
if (
result != null) {
return
result;
}
}
return new char[
minSize];
}
public void
freeMediumCBuffer(char[]
buffer)
{
if (
_currRecycler == null) {
_currRecycler =
createRecycler();
}
_currRecycler.
returnMediumCBuffer(
buffer);
}
public char[]
allocFullCBuffer(int
minSize)
{
if (
_currRecycler != null) {
char[]
result =
_currRecycler.
getFullCBuffer(
minSize);
if (
result != null) {
return
result;
}
}
return new char[
minSize];
}
public void
freeFullCBuffer(char[]
buffer)
{
// Need to create (and assign) the buffer?
if (
_currRecycler == null) {
_currRecycler =
createRecycler();
}
_currRecycler.
returnFullCBuffer(
buffer);
}
public byte[]
allocFullBBuffer(int
minSize)
{
if (
_currRecycler != null) {
byte[]
result =
_currRecycler.
getFullBBuffer(
minSize);
if (
result != null) {
return
result;
}
}
return new byte[
minSize];
}
public void
freeFullBBuffer(byte[]
buffer)
{
// Need to create (and assign) the buffer?
if (
_currRecycler == null) {
_currRecycler =
createRecycler();
}
_currRecycler.
returnFullBBuffer(
buffer);
}
private
BufferRecycler createRecycler()
{
BufferRecycler recycler = new
BufferRecycler();
// No way to reuse/reset SoftReference, have to create new always:
mRecyclerRef.
set(new
SoftReference<
BufferRecycler>(
recycler));
return
recycler;
}
/*
/**********************************************************************
/* Symbol table reusing, character types
/**********************************************************************
*/
public
WNameTable getUtf8Symbols(
WNameFactory f)
{
return
_encodingContext.
getUtf8Symbols(
f);
}
public
WNameTable getLatin1Symbols(
WNameFactory f)
{
return
_encodingContext.
getLatin1Symbols(
f);
}
public
WNameTable getAsciiSymbols(
WNameFactory f)
{
return
_encodingContext.
getAsciiSymbols(
f);
}
public
WNameTable getCharSymbols(
WNameFactory f)
{
return
_encodingContext.
getCharSymbols(
f);
}
/*
/**********************************************************************
/* Helper classes
/**********************************************************************
*/
/**
* This is a simple container class that is used to encapsulate
* per-factory encoding-dependant information like symbol tables.
*/
final static class
EncodingContext
{
WNameTable mUtf8Table;
WNameTable mLatin1Table;
WNameTable mAsciiTable;
WNameTable mCharTable;
EncodingContext() { }
public synchronized
WNameTable getUtf8Symbols(
WNameFactory f)
{
if (
mUtf8Table == null) {
mUtf8Table = new
WNameTable(64);
}
return
mUtf8Table.
createChild(
f);
}
public synchronized
WNameTable getLatin1Symbols(
WNameFactory f)
{
if (
mLatin1Table == null) {
mLatin1Table = new
WNameTable(64);
}
return
mLatin1Table.
createChild(
f);
}
public synchronized
WNameTable getAsciiSymbols(
WNameFactory f)
{
if (
mAsciiTable == null) {
mAsciiTable = new
WNameTable(64);
}
return
mAsciiTable.
createChild(
f);
}
public synchronized
WNameTable getCharSymbols(
WNameFactory f)
{
if (
mCharTable == null) {
mCharTable = new
WNameTable(64);
}
return
mCharTable.
createChild(
f);
}
}
}