blob: e4bc447da261c8324f3f11e65ff6e9ad4ae8b249 [file] [log] [blame]
/*
* 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.
*/
/*
* $Id: OutputProperties.java 468643 2006-10-28 06:56:03Z minchau $
*/
package org.apache.xalan.templates;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerException;
import org.apache.xalan.res.XSLMessages;
import org.apache.xalan.res.XSLTErrorResources;
import org.apache.xml.serializer.OutputPropertiesFactory;
import org.apache.xml.serializer.OutputPropertyUtils;
import org.apache.xml.utils.FastStringBuffer;
import org.apache.xml.utils.QName;
/**
* This class provides information from xsl:output elements. It is mainly
* a wrapper for {@link java.util.Properties}, but can not extend that class
* because it must be part of the {@link org.apache.xalan.templates.ElemTemplateElement}
* heararchy.
* <p>An OutputProperties list can contain another OutputProperties list as
* its "defaults"; this second property list is searched if the property key
* is not found in the original property list.</p>
* @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
* @see <a href="http://www.w3.org/TR/xslt#output">xsl:output in XSLT Specification</a>
*
*/
public class OutputProperties extends ElemTemplateElement
implements Cloneable
{
static final long serialVersionUID = -6975274363881785488L;
/**
* Creates an empty OutputProperties with no default values.
*/
public OutputProperties()
{
this(org.apache.xml.serializer.Method.XML);
}
/**
* Creates an empty OutputProperties with the specified defaults.
*
* @param defaults the defaults.
*/
public OutputProperties(Properties defaults)
{
m_properties = new Properties(defaults);
}
/**
* Creates an empty OutputProperties with the defaults specified by
* a property file. The method argument is used to construct a string of
* the form output_[method].properties (for instance, output_html.properties).
* The output_xml.properties file is always used as the base.
* <p>At the moment, anything other than 'text', 'xml', and 'html', will
* use the output_xml.properties file.</p>
*
* @param method non-null reference to method name.
*/
public OutputProperties(String method)
{
m_properties = new Properties(
OutputPropertiesFactory.getDefaultMethodProperties(method));
}
/**
* Clone this OutputProperties, including a clone of the wrapped Properties
* reference.
*
* @return A new OutputProperties reference, mutation of which should not
* effect this object.
*/
public Object clone()
{
try
{
OutputProperties cloned = (OutputProperties) super.clone();
cloned.m_properties = (Properties) cloned.m_properties.clone();
return cloned;
}
catch (CloneNotSupportedException e)
{
return null;
}
}
/**
* Set an output property.
*
* @param key the key to be placed into the property list.
* @param value the value corresponding to <tt>key</tt>.
* @see javax.xml.transform.OutputKeys
*/
public void setProperty(QName key, String value)
{
setProperty(key.toNamespacedString(), value);
}
/**
* Set an output property.
*
* @param key the key to be placed into the property list.
* @param value the value corresponding to <tt>key</tt>.
* @see javax.xml.transform.OutputKeys
*/
public void setProperty(String key, String value)
{
if(key.equals(OutputKeys.METHOD))
{
setMethodDefaults(value);
}
if (key.startsWith(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL))
key = OutputPropertiesFactory.S_BUILTIN_EXTENSIONS_UNIVERSAL
+ key.substring(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL_LEN);
m_properties.put(key, value);
}
/**
* Searches for the property with the specified key in the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>null</code> if the property is not found.
*
* @param key the property key.
* @return the value in this property list with the specified key value.
*/
public String getProperty(QName key)
{
return m_properties.getProperty(key.toNamespacedString());
}
/**
* Searches for the property with the specified key in the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>null</code> if the property is not found.
*
* @param key the property key.
* @return the value in this property list with the specified key value.
*/
public String getProperty(String key)
{
if (key.startsWith(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL))
key = OutputPropertiesFactory.S_BUILTIN_EXTENSIONS_UNIVERSAL
+ key.substring(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL_LEN);
return m_properties.getProperty(key);
}
/**
* Set an output property.
*
* @param key the key to be placed into the property list.
* @param value the value corresponding to <tt>key</tt>.
* @see javax.xml.transform.OutputKeys
*/
public void setBooleanProperty(QName key, boolean value)
{
m_properties.put(key.toNamespacedString(), value ? "yes" : "no");
}
/**
* Set an output property.
*
* @param key the key to be placed into the property list.
* @param value the value corresponding to <tt>key</tt>.
* @see javax.xml.transform.OutputKeys
*/
public void setBooleanProperty(String key, boolean value)
{
m_properties.put(key, value ? "yes" : "no");
}
/**
* Searches for the boolean property with the specified key in the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>false</code> if the property is not found, or if the value is other
* than "yes".
*
* @param key the property key.
* @return the value in this property list as a boolean value, or false
* if null or not "yes".
*/
public boolean getBooleanProperty(QName key)
{
return getBooleanProperty(key.toNamespacedString());
}
/**
* Searches for the boolean property with the specified key in the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>false</code> if the property is not found, or if the value is other
* than "yes".
*
* @param key the property key.
* @return the value in this property list as a boolean value, or false
* if null or not "yes".
*/
public boolean getBooleanProperty(String key)
{
return OutputPropertyUtils.getBooleanProperty(key, m_properties);
}
/**
* Set an output property.
*
* @param key the key to be placed into the property list.
* @param value the value corresponding to <tt>key</tt>.
* @see javax.xml.transform.OutputKeys
*/
public void setIntProperty(QName key, int value)
{
setIntProperty(key.toNamespacedString(), value);
}
/**
* Set an output property.
*
* @param key the key to be placed into the property list.
* @param value the value corresponding to <tt>key</tt>.
* @see javax.xml.transform.OutputKeys
*/
public void setIntProperty(String key, int value)
{
m_properties.put(key, Integer.toString(value));
}
/**
* Searches for the int property with the specified key in the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>false</code> if the property is not found, or if the value is other
* than "yes".
*
* @param key the property key.
* @return the value in this property list as a int value, or false
* if null or not a number.
*/
public int getIntProperty(QName key)
{
return getIntProperty(key.toNamespacedString());
}
/**
* Searches for the int property with the specified key in the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>false</code> if the property is not found, or if the value is other
* than "yes".
*
* @param key the property key.
* @return the value in this property list as a int value, or false
* if null or not a number.
*/
public int getIntProperty(String key)
{
return OutputPropertyUtils.getIntProperty(key, m_properties);
}
/**
* Set an output property with a QName value. The QName will be turned
* into a string with the namespace in curly brackets.
*
* @param key the key to be placed into the property list.
* @param value the value corresponding to <tt>key</tt>.
* @see javax.xml.transform.OutputKeys
*/
public void setQNameProperty(QName key, QName value)
{
setQNameProperty(key.toNamespacedString(), value);
}
/**
* Reset the default properties based on the method.
*
* @param method the method value.
* @see javax.xml.transform.OutputKeys
*/
public void setMethodDefaults(String method)
{
String defaultMethod = m_properties.getProperty(OutputKeys.METHOD);
if((null == defaultMethod) || !defaultMethod.equals(method)
// bjm - add the next condition as a hack
// but it is because both output_xml.properties and
// output_unknown.properties have the same method=xml
// for their default. Otherwise we end up with
// a ToUnknownStream wraping a ToXMLStream even
// when the users says method="xml"
//
|| defaultMethod.equals("xml")
)
{
Properties savedProps = m_properties;
Properties newDefaults =
OutputPropertiesFactory.getDefaultMethodProperties(method);
m_properties = new Properties(newDefaults);
copyFrom(savedProps, false);
}
}
/**
* Set an output property with a QName value. The QName will be turned
* into a string with the namespace in curly brackets.
*
* @param key the key to be placed into the property list.
* @param value the value corresponding to <tt>key</tt>.
* @see javax.xml.transform.OutputKeys
*/
public void setQNameProperty(String key, QName value)
{
setProperty(key, value.toNamespacedString());
}
/**
* Searches for the qname property with the specified key in the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>null</code> if the property is not found.
*
* @param key the property key.
* @return the value in this property list as a QName value, or false
* if null or not "yes".
*/
public QName getQNameProperty(QName key)
{
return getQNameProperty(key.toNamespacedString());
}
/**
* Searches for the qname property with the specified key in the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>null</code> if the property is not found.
*
* @param key the property key.
* @return the value in this property list as a QName value, or false
* if null or not "yes".
*/
public QName getQNameProperty(String key)
{
return getQNameProperty(key, m_properties);
}
/**
* Searches for the qname property with the specified key in the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>null</code> if the property is not found.
*
* @param key the property key.
* @param props the list of properties to search in.
* @return the value in this property list as a QName value, or false
* if null or not "yes".
*/
public static QName getQNameProperty(String key, Properties props)
{
String s = props.getProperty(key);
if (null != s)
return QName.getQNameFromString(s);
else
return null;
}
/**
* Set an output property with a QName list value. The QNames will be turned
* into strings with the namespace in curly brackets.
*
* @param key the key to be placed into the property list.
* @param v non-null list of QNames corresponding to <tt>key</tt>.
* @see javax.xml.transform.OutputKeys
*/
public void setQNameProperties(QName key, Vector v)
{
setQNameProperties(key.toNamespacedString(), v);
}
/**
* Set an output property with a QName list value. The QNames will be turned
* into strings with the namespace in curly brackets.
*
* @param key the key to be placed into the property list.
* @param v non-null list of QNames corresponding to <tt>key</tt>.
* @see javax.xml.transform.OutputKeys
*/
public void setQNameProperties(String key, Vector v)
{
int s = v.size();
// Just an initial guess at reasonable tuning parameters
FastStringBuffer fsb = new FastStringBuffer(9,9);
for (int i = 0; i < s; i++)
{
QName qname = (QName) v.elementAt(i);
fsb.append(qname.toNamespacedString());
// Don't append space after last value
if (i < s-1)
fsb.append(' ');
}
m_properties.put(key, fsb.toString());
}
/**
* Searches for the list of qname properties with the specified key in
* the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>null</code> if the property is not found.
*
* @param key the property key.
* @return the value in this property list as a vector of QNames, or false
* if null or not "yes".
*/
public Vector getQNameProperties(QName key)
{
return getQNameProperties(key.toNamespacedString());
}
/**
* Searches for the list of qname properties with the specified key in
* the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>null</code> if the property is not found.
*
* @param key the property key.
* @return the value in this property list as a vector of QNames, or false
* if null or not "yes".
*/
public Vector getQNameProperties(String key)
{
return getQNameProperties(key, m_properties);
}
/**
* Searches for the list of qname properties with the specified key in
* the property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns
* <code>null</code> if the property is not found.
*
* @param key the property key.
* @param props the list of properties to search in.
* @return the value in this property list as a vector of QNames, or false
* if null or not "yes".
*/
public static Vector getQNameProperties(String key, Properties props)
{
String s = props.getProperty(key);
if (null != s)
{
Vector v = new Vector();
int l = s.length();
boolean inCurly = false;
FastStringBuffer buf = new FastStringBuffer();
// parse through string, breaking on whitespaces. I do this instead
// of a tokenizer so I can track whitespace inside of curly brackets,
// which theoretically shouldn't happen if they contain legal URLs.
for (int i = 0; i < l; i++)
{
char c = s.charAt(i);
if (Character.isWhitespace(c))
{
if (!inCurly)
{
if (buf.length() > 0)
{
QName qname = QName.getQNameFromString(buf.toString());
v.addElement(qname);
buf.reset();
}
continue;
}
}
else if ('{' == c)
inCurly = true;
else if ('}' == c)
inCurly = false;
buf.append(c);
}
if (buf.length() > 0)
{
QName qname = QName.getQNameFromString(buf.toString());
v.addElement(qname);
buf.reset();
}
return v;
}
else
return null;
}
/**
* This function is called to recompose all of the output format extended elements.
*
* @param root non-null reference to the stylesheet root object.
*/
public void recompose(StylesheetRoot root)
throws TransformerException
{
root.recomposeOutput(this);
}
/**
* This function is called after everything else has been
* recomposed, and allows the template to set remaining
* values that may be based on some other property that
* depends on recomposition.
*/
public void compose(StylesheetRoot sroot) throws TransformerException
{
super.compose(sroot);
}
/**
* Get the Properties object that this class wraps.
*
* @return non-null reference to Properties object.
*/
public Properties getProperties()
{
return m_properties;
}
/**
* Copy the keys and values from the source to this object. This will
* not copy the default values. This is meant to be used by going from
* a higher precedence object to a lower precedence object, so that if a
* key already exists, this method will not reset it.
*
* @param src non-null reference to the source properties.
*/
public void copyFrom(Properties src)
{
copyFrom(src, true);
}
/**
* Copy the keys and values from the source to this object. This will
* not copy the default values. This is meant to be used by going from
* a higher precedence object to a lower precedence object, so that if a
* key already exists, this method will not reset it.
*
* @param src non-null reference to the source properties.
* @param shouldResetDefaults true if the defaults should be reset based on
* the method property.
*/
public void copyFrom(Properties src, boolean shouldResetDefaults)
{
Enumeration keys = src.keys();
while (keys.hasMoreElements())
{
String key = (String) keys.nextElement();
if (!isLegalPropertyKey(key))
throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{key})); //"output property not recognized: "
Object oldValue = m_properties.get(key);
if (null == oldValue)
{
String val = (String) src.get(key);
if(shouldResetDefaults && key.equals(OutputKeys.METHOD))
{
setMethodDefaults(val);
}
m_properties.put(key, val);
}
else if (key.equals(OutputKeys.CDATA_SECTION_ELEMENTS))
{
m_properties.put(key, (String) oldValue + " " + (String) src.get(key));
}
}
}
/**
* Copy the keys and values from the source to this object. This will
* not copy the default values. This is meant to be used by going from
* a higher precedence object to a lower precedence object, so that if a
* key already exists, this method will not reset it.
*
* @param opsrc non-null reference to an OutputProperties.
*/
public void copyFrom(OutputProperties opsrc)
throws TransformerException
{
// Bugzilla 6157: recover from xsl:output statements
// checkDuplicates(opsrc);
copyFrom(opsrc.getProperties());
}
/**
* Report if the key given as an argument is a legal xsl:output key.
*
* @param key non-null reference to key name.
*
* @return true if key is legal.
*/
public static boolean isLegalPropertyKey(String key)
{
return (key.equals(OutputKeys.CDATA_SECTION_ELEMENTS)
|| key.equals(OutputKeys.DOCTYPE_PUBLIC)
|| key.equals(OutputKeys.DOCTYPE_SYSTEM)
|| key.equals(OutputKeys.ENCODING)
|| key.equals(OutputKeys.INDENT)
|| key.equals(OutputKeys.MEDIA_TYPE)
|| key.equals(OutputKeys.METHOD)
|| key.equals(OutputKeys.OMIT_XML_DECLARATION)
|| key.equals(OutputKeys.STANDALONE)
|| key.equals(OutputKeys.VERSION)
|| (key.length() > 0)
&& (key.charAt(0) == '{')
&& (key.lastIndexOf('{') == 0)
&& (key.indexOf('}') > 0)
&& (key.lastIndexOf('}') == key.indexOf('}')));
}
/** The output properties.
* @serial */
private Properties m_properties = null;
/**
* Creates an empty OutputProperties with the defaults specified by
* a property file. The method argument is used to construct a string of
* the form output_[method].properties (for instance, output_html.properties).
* The output_xml.properties file is always used as the base.
* <p>At the moment, anything other than 'text', 'xml', and 'html', will
* use the output_xml.properties file.</p>
*
* @param method non-null reference to method name.
*
* @return Properties object that holds the defaults for the given method.
*
* @deprecated Use org.apache.xml.serializer.OuputPropertiesFactory.
* getDefaultMethodProperties directly.
*/
static public Properties getDefaultMethodProperties(String method)
{
return org.apache.xml.serializer.OutputPropertiesFactory.getDefaultMethodProperties(method);
}
}