1 /* 2 * $Header$ 3 * $Revision$ 4 * $Date$ 5 * 6 * ==================================================================== 7 * 8 * Copyright 2000-2002 bob mcwhirter & James Strachan. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions are 13 * met: 14 * 15 * * Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 18 * * Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * * Neither the name of the Jaxen Project nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 27 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 29 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 30 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 31 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 33 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 34 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 35 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 36 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 * 38 * ==================================================================== 39 * This software consists of voluntary contributions made by many 40 * individuals on behalf of the Jaxen Project and was originally 41 * created by bob mcwhirter <bob@werken.com> and 42 * James Strachan <jstrachan@apache.org>. For more information on the 43 * Jaxen Project, please see <http://www.jaxen.org/>. 44 * 45 * $Id$ 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 cite="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="https://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