/* * 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. *
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.
* @see XSLT DTD * @see xsl:output in XSLT Specification * */ 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. *At the moment, anything other than 'text', 'xml', and 'html', will * use the output_xml.properties file.
* * @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 key. * @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 key. * @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 *null
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
* null
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 key.
* @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 key.
* @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
* false
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
* false
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 key.
* @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 key.
* @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
* false
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
* false
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 key.
* @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 key.
* @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
* null
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
* null
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
* null
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 key.
* @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 key.
* @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
* null
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
* null
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
* null
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.
* At the moment, anything other than 'text', 'xml', and 'html', will * use the output_xml.properties file.
* * @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); } }