001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: ExtensionHandlerGeneral.java 469672 2006-10-31 21:56:19Z minchau $ 020 */ 021 package org.apache.xalan.extensions; 022 023 import java.io.IOException; 024 import java.io.InputStream; 025 import java.lang.reflect.Method; 026 import java.net.URL; 027 import java.net.URLConnection; 028 import java.util.Hashtable; 029 import java.util.Vector; 030 031 import javax.xml.transform.TransformerException; 032 033 import org.apache.xml.res.XMLErrorResources; 034 import org.apache.xml.res.XMLMessages; 035 036 import org.apache.xalan.res.XSLMessages; 037 import org.apache.xalan.res.XSLTErrorResources; 038 import org.apache.xalan.templates.ElemTemplateElement; 039 import org.apache.xalan.templates.Stylesheet; 040 import org.apache.xalan.transformer.TransformerImpl; 041 import org.apache.xml.dtm.DTMIterator; 042 import org.apache.xml.dtm.ref.DTMNodeList; 043 import org.apache.xml.utils.StringVector; 044 import org.apache.xml.utils.SystemIDResolver; 045 import org.apache.xpath.XPathProcessorException; 046 import org.apache.xpath.functions.FuncExtFunction; 047 import org.apache.xpath.objects.XObject; 048 049 /** 050 * Class handling an extension namespace for XPath. Provides functions 051 * to test a function's existence and call a function 052 * 053 * @author Sanjiva Weerawarana (sanjiva@watson.ibm.com) 054 * @xsl.usage internal 055 */ 056 public class ExtensionHandlerGeneral extends ExtensionHandler 057 { 058 059 /** script source to run (if any) */ 060 private String m_scriptSrc; 061 062 /** URL of source of script (if any) */ 063 private String m_scriptSrcURL; 064 065 /** functions of namespace */ 066 private Hashtable m_functions = new Hashtable(); 067 068 /** elements of namespace */ 069 private Hashtable m_elements = new Hashtable(); 070 071 // BSF objects used to invoke BSF by reflection. Do not import the BSF classes 072 // since we don't want a compile dependency on BSF. 073 074 /** BSF manager used to run scripts */ 075 private Object m_engine; 076 077 /** Engine call to invoke scripts */ 078 private Method m_engineCall = null; 079 080 // static fields 081 082 /** BSFManager package name */ 083 private static String BSF_MANAGER ; 084 085 /** Default BSFManager name */ 086 private static final String DEFAULT_BSF_MANAGER = "org.apache.bsf.BSFManager"; 087 088 /** Property name to load the BSFManager class */ 089 private static final String propName = "org.apache.xalan.extensions.bsf.BSFManager"; 090 091 /** Integer Zero */ 092 private static final Integer ZEROINT = new Integer(0); 093 094 static{ 095 BSF_MANAGER = ObjectFactory.lookUpFactoryClassName(propName, null, null); 096 097 if (BSF_MANAGER == null){ 098 BSF_MANAGER = DEFAULT_BSF_MANAGER; 099 } 100 } 101 102 /** 103 * Construct a new extension namespace handler given all the information 104 * needed. 105 * 106 * @param namespaceUri the extension namespace URI that I'm implementing 107 * @param elemNames Vector of element names 108 * @param funcNames string containing list of functions of extension NS 109 * @param scriptLang Scripting language of implementation 110 * @param scriptSrcURL URL of source script 111 * @param scriptSrc the actual script code (if any) 112 * @param systemId 113 * 114 * @throws TransformerException 115 */ 116 public ExtensionHandlerGeneral( 117 String namespaceUri, StringVector elemNames, StringVector funcNames, String scriptLang, String scriptSrcURL, String scriptSrc, String systemId) 118 throws TransformerException 119 { 120 121 super(namespaceUri, scriptLang); 122 123 if (elemNames != null) 124 { 125 Object junk = new Object(); 126 int n = elemNames.size(); 127 128 for (int i = 0; i < n; i++) 129 { 130 String tok = elemNames.elementAt(i); 131 132 m_elements.put(tok, junk); // just stick it in there basically 133 } 134 } 135 136 if (funcNames != null) 137 { 138 Object junk = new Object(); 139 int n = funcNames.size(); 140 141 for (int i = 0; i < n; i++) 142 { 143 String tok = funcNames.elementAt(i); 144 145 m_functions.put(tok, junk); // just stick it in there basically 146 } 147 } 148 149 m_scriptSrcURL = scriptSrcURL; 150 m_scriptSrc = scriptSrc; 151 152 if (m_scriptSrcURL != null) 153 { 154 URL url = null; 155 try{ 156 url = new URL(m_scriptSrcURL); 157 } 158 catch (java.net.MalformedURLException mue) 159 { 160 int indexOfColon = m_scriptSrcURL.indexOf(':'); 161 int indexOfSlash = m_scriptSrcURL.indexOf('/'); 162 163 if ((indexOfColon != -1) && (indexOfSlash != -1) 164 && (indexOfColon < indexOfSlash)) 165 { 166 // The url is absolute. 167 url = null; 168 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_COULD_NOT_FIND_EXTERN_SCRIPT, new Object[]{m_scriptSrcURL}), mue); //"src attribute not yet supported for " 169 //+ scriptLang); 170 } 171 else 172 { 173 try{ 174 url = new URL(new URL(SystemIDResolver.getAbsoluteURI(systemId)), m_scriptSrcURL); 175 } 176 catch (java.net.MalformedURLException mue2) 177 { 178 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_COULD_NOT_FIND_EXTERN_SCRIPT, new Object[]{m_scriptSrcURL}), mue2); //"src attribute not yet supported for " 179 //+ scriptLang); 180 } 181 } 182 } 183 if (url != null) 184 { 185 try 186 { 187 URLConnection uc = url.openConnection(); 188 InputStream is = uc.getInputStream(); 189 byte []bArray = new byte[uc.getContentLength()]; 190 is.read(bArray); 191 m_scriptSrc = new String(bArray); 192 193 } 194 catch (IOException ioe) 195 { 196 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_COULD_NOT_FIND_EXTERN_SCRIPT, new Object[]{m_scriptSrcURL}), ioe); //"src attribute not yet supported for " 197 //+ scriptLang); 198 } 199 } 200 201 } 202 203 Object manager = null; 204 try 205 { 206 manager = ObjectFactory.newInstance( 207 BSF_MANAGER, ObjectFactory.findClassLoader(), true); 208 } 209 catch (ObjectFactory.ConfigurationError e) 210 { 211 e.printStackTrace(); 212 } 213 214 if (manager == null) 215 { 216 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_INIT_BSFMGR, null)); //"Could not initialize BSF manager"); 217 } 218 219 try 220 { 221 Method loadScriptingEngine = manager.getClass() 222 .getMethod("loadScriptingEngine", new Class[]{ String.class }); 223 224 m_engine = loadScriptingEngine.invoke(manager, 225 new Object[]{ scriptLang }); 226 227 Method engineExec = m_engine.getClass().getMethod("exec", 228 new Class[]{ String.class, Integer.TYPE, Integer.TYPE, Object.class }); 229 230 // "Compile" the program 231 engineExec.invoke(m_engine, 232 new Object[]{ "XalanScript", ZEROINT, ZEROINT, m_scriptSrc }); 233 } 234 catch (Exception e) 235 { 236 e.printStackTrace(); 237 238 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_CMPL_EXTENSN, null), e); //"Could not compile extension", e); 239 } 240 } 241 242 /** 243 * Tests whether a certain function name is known within this namespace. 244 * @param function name of the function being tested 245 * @return true if its known, false if not. 246 */ 247 public boolean isFunctionAvailable(String function) 248 { 249 return (m_functions.get(function) != null); 250 } 251 252 /** 253 * Tests whether a certain element name is known within this namespace. 254 * @param element name of the element being tested 255 * @return true if its known, false if not. 256 */ 257 public boolean isElementAvailable(String element) 258 { 259 return (m_elements.get(element) != null); 260 } 261 262 /** 263 * Process a call to a function. 264 * 265 * @param funcName Function name. 266 * @param args The arguments of the function call. 267 * @param methodKey A key that uniquely identifies this class and method call. 268 * @param exprContext The context in which this expression is being executed. 269 * 270 * @return the return value of the function evaluation. 271 * 272 * @throws TransformerException if parsing trouble 273 */ 274 public Object callFunction( 275 String funcName, Vector args, Object methodKey, ExpressionContext exprContext) 276 throws TransformerException 277 { 278 279 Object[] argArray; 280 281 try 282 { 283 argArray = new Object[args.size()]; 284 285 for (int i = 0; i < argArray.length; i++) 286 { 287 Object o = args.get(i); 288 289 argArray[i] = (o instanceof XObject) ? ((XObject) o).object() : o; 290 o = argArray[i]; 291 if(null != o && o instanceof DTMIterator) 292 { 293 argArray[i] = new DTMNodeList((DTMIterator)o); 294 } 295 } 296 297 if (m_engineCall == null) { 298 m_engineCall = m_engine.getClass().getMethod("call", 299 new Class[]{ Object.class, String.class, Object[].class }); 300 } 301 302 return m_engineCall.invoke(m_engine, 303 new Object[]{ null, funcName, argArray }); 304 } 305 catch (Exception e) 306 { 307 e.printStackTrace(); 308 309 String msg = e.getMessage(); 310 311 if (null != msg) 312 { 313 if (msg.startsWith("Stopping after fatal error:")) 314 { 315 msg = msg.substring("Stopping after fatal error:".length()); 316 } 317 318 // System.out.println("Call to extension function failed: "+msg); 319 throw new TransformerException(e); 320 } 321 else 322 { 323 324 // Should probably make a TRaX Extension Exception. 325 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_CREATE_EXTENSN, new Object[]{funcName, e })); //"Could not create extension: " + funcName 326 //+ " because of: " + e); 327 } 328 } 329 } 330 331 /** 332 * Process a call to an XPath extension function 333 * 334 * @param extFunction The XPath extension function 335 * @param args The arguments of the function call. 336 * @param exprContext The context in which this expression is being executed. 337 * @return the return value of the function evaluation. 338 * @throws TransformerException 339 */ 340 public Object callFunction(FuncExtFunction extFunction, 341 Vector args, 342 ExpressionContext exprContext) 343 throws TransformerException 344 { 345 return callFunction(extFunction.getFunctionName(), args, 346 extFunction.getMethodKey(), exprContext); 347 } 348 349 /** 350 * Process a call to this extension namespace via an element. As a side 351 * effect, the results are sent to the TransformerImpl's result tree. 352 * 353 * @param localPart Element name's local part. 354 * @param element The extension element being processed. 355 * @param transformer Handle to TransformerImpl. 356 * @param stylesheetTree The compiled stylesheet tree. 357 * @param methodKey A key that uniquely identifies this class and method call. 358 * 359 * @throws XSLProcessorException thrown if something goes wrong 360 * while running the extension handler. 361 * @throws MalformedURLException if loading trouble 362 * @throws FileNotFoundException if loading trouble 363 * @throws IOException if loading trouble 364 * @throws TransformerException if parsing trouble 365 */ 366 public void processElement( 367 String localPart, ElemTemplateElement element, TransformerImpl transformer, 368 Stylesheet stylesheetTree, Object methodKey) 369 throws TransformerException, IOException 370 { 371 372 Object result = null; 373 XSLProcessorContext xpc = new XSLProcessorContext(transformer, stylesheetTree); 374 375 try 376 { 377 Vector argv = new Vector(2); 378 379 argv.add(xpc); 380 argv.add(element); 381 382 result = callFunction(localPart, argv, methodKey, 383 transformer.getXPathContext().getExpressionContext()); 384 } 385 catch (XPathProcessorException e) 386 { 387 388 // e.printStackTrace (); 389 throw new TransformerException(e.getMessage(), e); 390 } 391 392 if (result != null) 393 { 394 xpc.outputToResultTree(stylesheetTree, result); 395 } 396 } 397 }