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