WW-4731 Moves detailed description to wiki

This commit is contained in:
Lukasz Lenart
2017-01-08 20:29:58 +01:00
parent 97419283ea
commit 14d56fbb93
@@ -43,162 +43,8 @@ import java.net.URL;
import java.util.HashMap;
import java.util.Map;
/**
* <!-- START SNIPPET: description -->
*
* XSLTResult uses XSLT to transform an action object to XML. The recent version
* has been specifically modified to deal with Xalan flaws. When using Xalan you
* may notice that even though you have a very minimal stylesheet like this one
* <pre>
* &lt;xsl:template match="/result"&gt;
* &lt;result/&gt;
* &lt;/xsl:template&gt;</pre>
*
* <p>
* Xalan would still iterate through every property of your action and all
* its descendants.
* </p>
*
* <p>
* If you had double-linked objects, Xalan would work forever analysing an
* infinite object tree. Even if your stylesheet was not constructed to process
* them all. It's because the current Xalan eagerly and extensively converts
* everything to its internal DTM model before further processing.
* </p>
*
* <p>
* That's why there's a loop eliminator added that works by indexing every
* object-property combination during processing. If it notices that some
* object's property was already walked through, it doesn't go any deeper.
* Say you have two objects, x and y, with the following properties set
* (pseudocode):
* </p>
* <pre>
* x.y = y;
* and
* y.x = x;
* action.x=x;</pre>
*
* <p>
* Due to that modification, the resulting XML document based on x would be:
* </p>
*
* <pre>
* &lt;result&gt;
* &lt;x&gt;
* &lt;y/&gt;
* &lt;/x&gt;
* &lt;/result&gt;</pre>
*
* <p>
* Without it there would be endless x/y/x/y/x/y/... elements.
* </p>
*
* <p>
* The XSLTResult code tries also to deal with the fact that DTM model is built
* in a manner that children are processed before siblings. The result is that if
* there is object x that is both set in action's x property, and very deeply
* under action's a property then it would only appear under a, not under x.
* That's not what we expect, and that's why XSLTResult allows objects to repeat
* in various places to some extent.
* </p>
*
* <p>
* Sometimes the object mesh is still very dense and you may notice that even
* though you have a relatively simple stylesheet, execution takes a tremendous
* amount of time. To help you to deal with that obstacle of Xalan, you may
* attach regexp filters to elements paths (xpath).
* </p>
*
* <p>
* <b>Note:</b> In your .xsl file the root match must be named <tt>result</tt>.
* <br>This example will output the username by using <tt>getUsername</tt> on your
* action class:
* <pre>
* &lt;xsl:template match="result"&gt;
* &lt;html&gt;
* &lt;body&gt;
* Hello &lt;xsl:value-of select="username"/&gt; how are you?
* &lt;/body&gt;
* &lt;/html&gt;
* &lt;/xsl:template&gt;
* </pre>
*
* <p>
* In the following example the XSLT result would only walk through action's
* properties without their childs. It would also skip every property that has
* "hugeCollection" in their name. Element's path is first compared to
* excludingPattern - if it matches it's no longer processed. Then it is
* compared to matchingPattern and processed only if there's a match.
* </p>
*
* <!-- END SNIPPET: description -->
*
* <pre><!-- START SNIPPET: description.example -->
* &lt;result name="success" type="xslt"&gt;
* &lt;param name="location"&gt;foo.xslt&lt;/param&gt;
* &lt;param name="matchingPattern"&gt;^/result/[^/*]$&lt;/param&gt;
* &lt;param name="excludingPattern"&gt;.*(hugeCollection).*&lt;/param&gt;
* &lt;/result&gt;
* <!-- END SNIPPET: description.example --></pre>
*
* <p>
* In the following example the XSLT result would use the action's user property
* instead of the action as it's base document and walk through it's properties.
* The exposedValue uses an ognl expression to derive it's value.
* </p>
*
* <pre>
* &lt;result name="success" type="xslt"&gt;
* &lt;param name="location"&gt;foo.xslt&lt;/param&gt;
* &lt;param name="exposedValue"&gt;user$&lt;/param&gt;
* &lt;/result&gt;
* </pre>
* *
* <b>This result type takes the following parameters:</b>
*
* <!-- START SNIPPET: params -->
*
* <ul>
*
* <li><b>location (default)</b> - the location to go to after execution.</li>
* <li><b>encoding</b> - character encoding used in XML, default UTF-8.</li>
*
* <li><b>parse</b> - true by default. If set to false, the location param will
* not be parsed for Ognl expressions.</li>
*
* <!--
* <li><b>matchingPattern</b> - Pattern that matches only desired elements, by
* default it matches everything.</li>
*
* <li><b>excludingPattern</b> - Pattern that eliminates unwanted elements, by
* default it matches none.</li>
* -->
*
* </ul>
*
* <p>
* <code>struts.properties</code> related configuration:
* </p>
* <ul>
*
* <li><b>struts.xslt.nocache</b> - Defaults to false. If set to true, disables
* stylesheet caching. Good for development, bad for production.</li>
*
* </ul>
*
* <!-- END SNIPPET: params -->
* <p>
* <b>Example:</b>
* </p>
*
* <pre>
* <!-- START SNIPPET: example -->
* &lt;result name="success" type="xslt"&gt;foo.xslt&lt;/result&gt;
* <!-- END SNIPPET: example -->
* </pre>
*
* XSLTResult uses XSLT to transform an action object to XML.
*/
public class XSLTResult implements Result {
@@ -233,7 +79,7 @@ public class XSLTResult implements Result {
/** Indicates the property name patterns which should be excluded from the xml. */
private String excludingPattern;
/** Indicates the ognl expression respresenting the bean which is to be exposed as xml. */
/** Indicates the ognl expression representing the bean which is to be exposed as xml. */
private String exposedValue;
/** Indicates the status to return in the response */
@@ -258,8 +104,6 @@ public class XSLTResult implements Result {
}
public void setStylesheetLocation(String location) {
if (location == null)
throw new IllegalArgumentException("Null location");
this.stylesheetLocation = location;
}
@@ -306,12 +150,15 @@ public class XSLTResult implements Result {
long startTime = System.currentTimeMillis();
String location = getStylesheetLocation();
if (location == null) {
throw new IllegalArgumentException("Parameter 'stylesheetLocation' cannot be null!");
}
if (parse) {
ValueStack stack = ActionContext.getContext().getValueStack();
location = TextParseUtil.translateVariables(location, stack);
}
try {
HttpServletResponse response = ServletActionContext.getResponse();
response.setStatus(status);
@@ -331,10 +178,12 @@ public class XSLTResult implements Result {
transformer.setErrorListener(buildErrorListener());
String mimeType;
if (templates == null)
if (templates == null) {
mimeType = "text/xml"; // no stylesheet, raw xml
else
} else {
mimeType = templates.getOutputProperties().getProperty(OutputKeys.MEDIA_TYPE);
}
if (mimeType == null) {
// guess (this is a servlet, so text/html might be the best guess)
mimeType = "text/html";
@@ -383,8 +232,9 @@ public class XSLTResult implements Result {
}
protected AdapterFactory getAdapterFactory() {
if (adapterFactory == null)
if (adapterFactory == null) {
adapterFactory = new AdapterFactory();
}
return adapterFactory;
}
@@ -428,8 +278,7 @@ public class XSLTResult implements Result {
return templates;
}
protected Source getDOMSourceForStack(Object value)
throws IllegalAccessException, InstantiationException {
protected Source getDOMSourceForStack(Object value) throws IllegalAccessException, InstantiationException {
return new DOMSource(getAdapterFactory().adaptDocument("result", value) );
}
}