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 package org.jaxen.function;
64
65 import java.util.Iterator;
66 import java.util.List;
67
68 import org.jaxen.Context;
69 import org.jaxen.Function;
70 import org.jaxen.FunctionCallException;
71 import org.jaxen.Navigator;
72 import org.jaxen.UnsupportedAxisException;
73
74 /***
75 * <p>
76 * <b>4.3</b> <code><i>boolean</i> lang(<i>string</i>)</code>
77 * </p>
78 *
79 * <blockquote src="http://www.w3.org/TR/xpath#function-lang">
80 * <p>
81 * The <b>lang</b> function returns true or false depending on whether
82 * the language of the context node as specified by
83 * <code>xml:lang</code> attributes is the same as or is a sublanguage
84 * of the language specified by the argument string. The language of the
85 * context node is determined by the value of the <code>xml:lang</code>
86 *
87 * attribute on the context node, or, if the context node has no
88 * <code>xml:lang</code> attribute, by the value of the
89 * <code>xml:lang</code> attribute on the nearest ancestor of the
90 * context node that has an <code>xml:lang</code> attribute. If there
91 * is no such attribute, then <b><a href="#function-lang">lang</a></b>
92 * returns false. If there is such an attribute, then <b><a
93 * href="#function-lang">lang</a></b> returns true if the attribute
94 * value is equal to the argument ignoring case, or if there is some
95 * suffix starting with <code>-</code> such that the attribute value
96 * is equal to the argument ignoring that suffix of the attribute value
97 * and ignoring case. For example, <code>lang("en")</code> would
98 * return true if the context node is any of these five elements:
99 * </p>
100 *
101 * <pre>
102 * <para xml:lang="en"/>
103 * <div xml:lang="en"><para/></div>
104 * <para xml:lang="EN"/>
105 * <para xml:lang="en-us"/>
106 * </pre>
107 *
108 * </blockquote>
109 *
110 * @author Attila Szegedi (szegedia @ freemail.hu)
111 * @see <a href="http://www.w3.org/TR/xpath#function-lang"
112 * target="_top">XPath Specification</a>
113 */
114 public class LangFunction implements Function
115 {
116 private static final String LANG_LOCALNAME = "lang";
117 private static final String XMLNS_URI =
118 "http://www.w3.org/XML/1998/namespace";
119
120
121 /***
122 * Create a new <code>LangFunction</code> object.
123 */
124 public LangFunction() {}
125
126
127 /***
128 * <p>
129 * Determines whether or not the context node is written in the language specified
130 * by the XPath string-value of <code>args.get(0)</code>,
131 * as determined by the nearest <code>xml:lang</code> attribute in scope.
132 * </p>
133 *
134 * @param context the context in which to evaluate the <code>lang()</code> function
135 * @param args the arguments to the lang function
136 * @return a <code>Boolean</code> indicating whether the context node is written in
137 * the specified language
138 * @throws FunctionCallException if <code>args</code> does not have length one
139 *
140 */
141 public Object call(Context context,
142 List args) throws FunctionCallException
143 {
144 if (args.size() != 1) {
145 throw new FunctionCallException("lang() requires exactly one argument.");
146 }
147
148 Object arg = args.get(0);
149
150 try {
151 return evaluate(context.getNodeSet(), arg, context.getNavigator() );
152 }
153 catch(UnsupportedAxisException e) {
154 throw new FunctionCallException("Can't evaluate lang()",
155 e);
156 }
157
158 }
159
160 private static Boolean evaluate(List contextNodes, Object lang, Navigator nav)
161 throws UnsupportedAxisException
162 {
163 return evaluate(contextNodes.get(0),
164 StringFunction.evaluate(lang, nav), nav)
165 ? Boolean.TRUE : Boolean.FALSE;
166 }
167
168 private static boolean evaluate(Object node, String lang, Navigator nav)
169 throws UnsupportedAxisException
170 {
171
172 Object element = node;
173 if (! nav.isElement(element)) {
174 element = nav.getParentNode(node);
175 }
176 while (element != null && nav.isElement(element))
177 {
178 Iterator attrs = nav.getAttributeAxisIterator(element);
179 while(attrs.hasNext())
180 {
181 Object attr = attrs.next();
182 if(LANG_LOCALNAME.equals(nav.getAttributeName(attr)) &&
183 XMLNS_URI.equals(nav.getAttributeNamespaceUri(attr)))
184 {
185 return
186 isSublang(nav.getAttributeStringValue(attr), lang);
187 }
188 }
189 element = nav.getParentNode(element);
190 }
191 return false;
192 }
193
194 private static boolean isSublang(String sublang, String lang)
195 {
196 if(sublang.equalsIgnoreCase(lang))
197 {
198 return true;
199 }
200 int ll = lang.length();
201 return
202 sublang.length() > ll &&
203 sublang.charAt(ll) == '-' &&
204 sublang.substring(0, ll).equalsIgnoreCase(lang);
205 }
206
207 }
208