-
-
Notifications
You must be signed in to change notification settings - Fork 8.6k
/
MorphTagLibrary.java
124 lines (113 loc) · 5.45 KB
/
MorphTagLibrary.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package hudson.util.jelly;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.Tag;
import org.apache.commons.jelly.TagLibrary;
import org.apache.commons.jelly.XMLOutput;
import org.apache.commons.jelly.expression.Expression;
import org.apache.commons.jelly.impl.ExpressionAttribute;
import org.apache.commons.jelly.impl.TagScript;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/**
* Jelly tag library for literal-like tags, with an ability to add arbitrary attributes taken from a map.
*
* <p>
* Tags from this namespace ("jelly:hudson.util.jelly.MorphTagLibrary") behaves mostly like literal static tags,
* except it interprets two attributes "ATTRIBUTES" and "EXCEPT" in a special way.
*
* The "ATTRIBUTES" attribute should have a Jelly expression that points to a {@link Map} object,
* and the contents of the map are added as attributes of this tag, with the exceptions of entries whose key
* values are listed in the "EXCEPT" attribute.
*
* The "EXCEPT" attribute takes a white-space separated list of attribute names that should be ignored even
* if it's in the map.
*
* <p>
* The explicit literal attributes, if specified, always take precedence over the dynamic attributes added by the map.
*
* <p>
* See textbox.jelly as an example of using this tag library.
*
* @author Kohsuke Kawaguchi
* @since 1.342
*/
public class MorphTagLibrary extends TagLibrary {
/**
* This code is really only used for dealing with dynamic tag libraries, so no point in implementing
* this for statically used tag libraries.
*/
@Override
public Tag createTag(final String name, Attributes attributes) throws JellyException {
return null;
}
@Override
public TagScript createTagScript(final String tagName, Attributes attributes) throws JellyException {
return new TagScript() {
private Object evalAttribute(String name, JellyContext context) {
ExpressionAttribute e = attributes.get(name);
if (e == null) return null;
return e.exp.evaluate(context);
}
private Collection<?> getExclusions(JellyContext context) {
Object exclusion = evalAttribute(EXCEPT_ATTRIBUTES, context);
if (exclusion == null)
return Collections.emptySet();
if (exclusion instanceof String)
return Arrays.asList(exclusion.toString().split("\\s+")); // split by whitespace
if (exclusion instanceof Collection)
return (Collection) exclusion;
throw new IllegalArgumentException("Expected collection for exclusion but found :" + exclusion);
}
@Override
public void run(JellyContext context, XMLOutput output) throws JellyTagException {
AttributesImpl actual = new AttributesImpl();
Collection<?> exclusions = getExclusions(context);
Map<String, ?> meta = (Map) evalAttribute(META_ATTRIBUTES, context);
if (meta != null) {
for (Map.Entry<String, ?> e : meta.entrySet()) {
String key = e.getKey();
// @see jelly.impl.DynamicTag.setAttribute() -- ${attrs} has duplicates with "Attr" suffix
if (key.endsWith("Attr") && meta.containsKey(key.substring(0, key.length() - 4))) continue;
// @see http://github.com/jenkinsci/jelly/commit/4ae67d15957b5b4d32751619997a3cb2a6ad56ed
if (key.equals("ownerTag")) continue;
if (!exclusions.contains(key)) {
Object v = e.getValue();
if (v != null)
actual.addAttribute("", key, key, "CDATA", v.toString());
}
}
} else {
meta = Collections.emptyMap();
}
for (Map.Entry<String, ExpressionAttribute> e : attributes.entrySet()) {
String name = e.getKey();
if (name.equals(META_ATTRIBUTES) || name.equals(EXCEPT_ATTRIBUTES)) continue; // already handled
if (meta.containsKey(name)) {
// if the explicit value is also generated by a map, delete it first.
// this is O(N) operation, but we don't expect there to be a lot of collisions.
int idx = actual.getIndex(name);
if (idx >= 0) actual.removeAttribute(idx);
}
Expression expression = e.getValue().exp;
actual.addAttribute("", name, name, "CDATA", expression.evaluateAsString(context));
}
try {
output.startElement(tagName, actual);
getTagBody().run(context, output);
output.endElement(tagName);
} catch (SAXException x) {
throw new JellyTagException(x);
}
}
};
}
private static final String META_ATTRIBUTES = "ATTRIBUTES";
private static final String EXCEPT_ATTRIBUTES = "EXCEPT";
}